gli*_*a93 10 event-driven reactjs react-native react-hooks
我想在一个组件中“触发一个事件”,让其他组件“订阅”该事件并在 React 中做一些工作。
例如,这是一个典型的 React 项目。
我有一个模型,从服务器获取数据,并且使用该数据呈现多个组件。
interface Model {
id: number;
value: number;
}
const [data, setData] = useState<Model[]>([]);
useEffect(() => {
fetchDataFromServer().then((resp) => setData(resp.data));
}, []);
<Root>
<TopTab>
<Text>Model with large value count: {data.filter(m => m.value > 5).length}</Text>
</TobTab>
<Content>
<View>
{data.map(itemData: model, index: number) => (
<Item key={itemData.id} itemData={itemData} />
)}
</View>
</Content>
<BottomTab data={data} />
</Root>
Run Code Online (Sandbox Code Playgroud)
在一个子组件中,可以编辑和保存模型。
const [editItem, setEditItem] = useState<Model|null>(null);
<Root>
<TopTab>
<Text>Model with large value count: {data.filter(m => m.value > 5).length}</Text>
</TobTab>
<ListScreen>
{data.map(itemData: model, index: number) => (
<Item
key={itemData.id}
itemData={itemData}
onClick={() => setEditItem(itemData)}
/>
)}
</ListScreen>
{!!editItem && (
<EditScreen itemData={editItem} />
)}
<BottomTab data={data} />
</Root>
Run Code Online (Sandbox Code Playgroud)
让我们假设它是 EditScreen:
const [model, setModel] = useState(props.itemData);
<Input
value={model.value}
onChange={(value) => setModel({...model, Number(value)})}
/>
<Button
onClick={() => {
callSaveApi(model).then((resp) => {
setModel(resp.data);
// let other components know that this model is updated
})
}}
/>
Run Code Online (Sandbox Code Playgroud)
应用程序必须让TopTab,BottomTab和ListScreen组件更新数据
updateData函数作为 props传递(因为在大多数实际情况下,组件结构太复杂,无法将所有函数作为 props 传递)为了有效地解决上述问题,我想用一个参数(更改模型)触发一个事件(例如“模型更新”),并让其他组件订阅该事件并更改它们的数据,例如:
// in EditScreen
updateData().then(resp => {
const newModel = resp.data;
setModel(newModel);
Event.emit("model-updated", newModel);
});
// in any other components
useEffect(() => {
// subscribe model change event
Event.on("model-updated", (newModel) => {
doSomething(newModel);
});
// unsubscribe events on destroy
return () => {
Event.off("model-updated");
}
}, []);
// in another component
useEffect(() => {
// subscribe model change event
Event.on("model-updated", (newModel) => {
doSomethingDifferent(newModel);
});
// unsubscribe events on destroy
return () => {
Event.off("model-updated");
}
}, []);
Run Code Online (Sandbox Code Playgroud)
是否可以使用 React 钩子?
如何在 React 钩子中实现事件驱动的方法?
Muh*_*man 12
不可能有事件发射器的替代品,因为 React hooks 和使用上下文依赖于 dom 树深度并且范围有限。
将 EventEmitter 与 React(或 React Native)一起使用是否被认为是一个好的实践?
A:是的,当 dom 树深处有组件时,这是一个很好的方法
我正在 React 中寻找事件驱动的方法。我现在对我的解决方案很满意,但是我可以使用 React hooks 实现同样的效果吗?
答:如果您指的是组件状态,那么钩子将无法帮助您在组件之间共享它。组件状态是组件的本地状态。如果你的状态存在于上下文中,那么 useContext 钩子会很有帮助。对于 useContext,我们必须使用 和 实现完整的上下文 API,MyContext.Provider并且MyContext.Consumer必须包装在高阶 ( HOC ) 组件
Ref中
所以事件发射器是最好的。
在React Native中,您可以使用react-native-event-listeners包
yarn add react-native-event-listeners
Run Code Online (Sandbox Code Playgroud)
发送器组件
import { EventRegister } from 'react-native-event-listeners'
const Sender = (props) => (
<TouchableHighlight
onPress={() => {
EventRegister.emit('myCustomEvent', 'it works!!!')
})
><Text>Send Event</Text></TouchableHighlight>
)
Run Code Online (Sandbox Code Playgroud)
接收器组件
class Receiver extends PureComponent {
constructor(props) {
super(props)
this.state = {
data: 'no data',
}
}
componentWillMount() {
this.listener = EventRegister.addEventListener('myCustomEvent', (data) => {
this.setState({
data,
})
})
}
componentWillUnmount() {
EventRegister.removeEventListener(this.listener)
}
render() {
return <Text>{this.state.data}</Text>
}
}
Run Code Online (Sandbox Code Playgroud)
不知道为什么 EventEmitter 被否决了,但这是我的看法:
当谈到状态管理时,我相信使用基于 Flux 的方法通常是最佳选择(Context/Redux 和朋友都很棒)。也就是说,我真的不明白为什么基于事件的方法会带来任何问题 - JS是基于事件的,而 React 毕竟只是一个库,甚至不是一个框架,而且我不明白为什么我们会被迫这样做遵守其指导方针。
如果您的 UI 需要了解应用程序的一般状态并对其做出反应,请使用减速器,更新您的存储,然后使用 Context/Redux/Flux/无论什么 - 如果您只需要对特定事件做出反应,请使用 EventEmitter。
使用 EventEmitter 将允许您在 React 和其他库之间进行通信,例如画布(如果您没有使用 React Three Fiber,我敢说您可以尝试在没有事件的情况下与 ThreeJS/WebGL 进行交流),而无需使用所有样板。在很多情况下,使用 Context 是一场噩梦,我们不应该感到受到 React API 的限制。
如果它适合您,并且具有可扩展性,那就去做吧。
编辑:这是一个使用 eventemitter3 的示例:
./发射器.ts
import EventEmitter from 'eventemitter3';
const eventEmitter = new EventEmitter();
const Emitter = {
on: (event, fn) => eventEmitter.on(event, fn),
once: (event, fn) => eventEmitter.once(event, fn),
off: (event, fn) => eventEmitter.off(event, fn),
emit: (event, payload) => eventEmitter.emit(event, payload)
}
Object.freeze(Emitter);
export default Emitter;
Run Code Online (Sandbox Code Playgroud)
./some-component.ts
import Emitter from '.emitter';
export const SomeComponent = () => {
useEffect(() => {
// you can also use `.once()` to only trigger it ... once
Emitter.on('SOME_EVENT', () => do what you want here)
return () => {
Emitter.off('SOME_EVENT')
}
})
}
Run Code Online (Sandbox Code Playgroud)
从那里,您可以在任何您想要的地方触发事件,订阅它们,并对其采取行动,传递一些数据,做任何您真正想做的事情。
| 归档时间: |
|
| 查看次数: |
4330 次 |
| 最近记录: |