reactjs - 创建一个可以通过函数调用显示的 React 组件(如 react-toastif

通常,当创建一个我们想要有条件地渲染的可重用 React 组件时,我们要么给它一个 prop 来告诉它是否渲染自己:

function TheComponent(props) {
    return(
        props.isVisible?<div>...</div>:null
    );
}

或者从外部有条件地渲染整个组件:

function App() {
    //...
    return (
            isVisible ? <TheComponent /> : null
        );
}

或者,如果我们想要制作一个组件,我们可以在应用程序的任何地方显示/隐藏 - 比如 toast notification - 我们可以包装一个提供者并制作一个自定义钩子(Hook)来访问它的上下文;这将使我们能够在 provider 的任何地方显示/隐藏它,只需调用一个函数:

const App = () => (
  <ToastProvider>
    <OtherStuff />
  </ToastProvider>
);

const OtherStuff = () => {
  const { showToast } = useToast();
  showToast();
  return ...;
};

但是,有一个非常酷的包,react-toastify ,我似乎无法理解它是如何实现的。您所要做的就是放下 <ToastContainer />在您应用的某处,然后从其他任何地方,您可以:

import { toast } from "react-toastify";
toast.info("this will show the component with a message");

由于可以在提供程序外部调用此函数,我不太明白它是如何控制树中其他地方的组件状态的。我已经尝试查看它的代码,但作为 React 初学者,它有点让我头疼。我喜欢完全独立的组件的想法,您可以将其粘贴在应用程序的某个位置,并通过从任何地方调用函数来调用。没有 Provider/wrapper 或任何东西:只是一个函数调用,然后弹出。

有人可以帮助阐明这样的组件从根本上是如何工作的吗?提供程序外部的功能如何控制另一个组件内部的状态?

最佳答案

浏览 react-toastify code你可以看到它使用了事件发射器模式。 <ToastContainer /> listens for events那get dispatched (或发射)当你打电话时 toast.info .当它收到一个事件时,它会更新其内部状态(大概)以显示消息。


TLDR:他们通过 eventManager 间接通信,它公开了用于 1) 调度事件和 2) 为这些事件注册监听器的方法。


它类似于 onclick 处理程序在 DOM 中的工作方式。

这是基本模式的一个非常基本的实现:它只是在每次单击按钮时向文档附加一个 div。 (这不是特定于 React 或 toastify 的。但它展示了核心思想。)

请注意,按钮的点击处理程序对发生的事情一无所知。它不附加 div。它只是通过 EventBus 发出一个事件下面描述的实例。

EventBus类提供了一个 on注册监听器的方法。这些通常称为 addEventListener 或 addListener,或者它们具有特定于事件的名称,如 onClick、onChange 等,但它们都做同样的基本事情:注册一个要调用的函数以响应事件。 (这个类本质上是 react-toastify 的 eventManager 的一个愚蠢的实现。)

on方法将提供的处理程序添加到内部数组。然后,当一个事件被触发时(通过 emit )它只是遍历数组调用每个事件并传递事件信息。

const container = document.getElementById('demo');
const button = document.querySelector('button');

class EventBus {
  handlers = [];
  
  on (handler) {
    this.handlers.push(handler);
  }
  
  emit (event) {
    this.handlers.forEach(h => h(event));
  }
}

const emitter = new EventBus();

emitter.on((event) => {
  container.innerHTML += `<div>${event}</div>`;
})

button.addEventListener('click', () => emitter.emit('Button Clicked'));
<button>Emit</button>
<div id="demo"></div>

通过此设置,您可以添加额外的监听器来执行其他操作,而无需知道事件的来源(按钮单击)。下面的演示与上面的相同,只是它添加了一个额外的处理程序来切换“暗”模式。

再次注意,按钮不知道深色模式,深色模式处理程序也不知道按钮,它们都不知道附加的 div。它们完全分离。

这基本上就是 ToastContainer 的方式与 toast.info 一起工作.

const container = document.getElementById('demo');
const button = document.querySelector('button');

class EventBus {
  handlers = [];
  
  on (handler) {
    this.handlers.push(handler);
  }
  
  emit (event) {
    this.handlers.forEach(h => h(event));
  }
}

const emitter = new EventBus();

emitter.on((event) => {
  container.innerHTML += `<div>${event}</div>`;
})

button.addEventListener('click', () => emitter.emit('Button Clicked'));

// add an additional handler
emitter.on(event => demo.classList.toggle('dark'));
.dark {
  background: black;
  color: white;
}
<button>Emit</button>
<div id="demo"></div>

关于reactjs - 创建一个可以通过函数调用显示的 React 组件(如 react-toastify,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67443376/

相关文章:

visual-studio-code - 删除 VSCode 中的 Sublime 文本主题

react-native - 部署 key 为空 appcenter 代码推送 cli

regex - 如何将 RegEx token 传递给 RegEx 替换中的 PowerShell

python - 有没有办法缩短多个 if 语句?

amazon-web-services - CloudFormation YAML - 带有条件语句

c++ - 在 C++ 中定义类枚举值的 std::vector 的缩写语法

r - 将列中以冒号分隔的字符串拆分为 R 中的不同列

python - 基于字典值映射Python列表

python - 将 VSCode 更新到 1.56.1 后,出现错误 : "Cannot acti

fortran - Fortran 中的嵌套名单