在子父关系中的组件之间共享数据的过程在 React 文档中有详细记录和直接处理。不太明显的是如何在不共享子父关系的组件之间共享状态和任意数据的公认方式。 Flux 是作为一种解决方案提供的,过去我已经推出了自己的 pub/sub 系统,但在这个领域,reactjs 开发人员之间似乎仍然存在很大分歧。 RxJS 已经作为一种解决方案提供,并且观察者模式有许多变体,但我想知道是否有一种更规范的方法来管理这个问题,特别是在组件绑定不太紧密的大型应用程序中。
我的解决方案通常是将回调作为 prop 传递给将接受用户输入的组件。回调在父级中执行状态更改,并向下传播。例如:
UI = React.createClass({
getInitialState() {
return {
text: ""
};
}
hello(text) {
this.setState({
text: text
});
}
render() {
return (
<div>
<TextView content={this.state.text}>
<Button onclick={() => this.hello("HELLO WORLD")}>
</div>
);
}
});
// TextView and Button are left as an exercise to the reader
我喜欢这一点,因为每个父组件仍然对其内容唯一负责,并且子组件不会侵入性地了解彼此的任何信息;都是回调。诚然,它可能无法很好地扩展到“大规模”反应应用程序,但我喜欢没有全局事件调度程序来管理所有内容,这使得控制流难以遵循。在此示例中,UI
类将始终是完全独立的,并且可以根据需要进行复制,而不会有任何名称重用的风险。
它可能不是100%你需要的,但我认为它是一个非常有用的功能。我在下面的一个简单的应用程序中展示了如何使用它。但可接受的用法请参阅官方文档。
//App.js
import {useReducer} from 'react';
import { StyleSheet, Text, View } from 'react-native';
import {SiteContext, SiteDispatchContext, siteReducer, siteInitialState} from "./components/context";
import List from './components/list'
export default function App() {
const [state, reducer] = useReducer(siteReducer, siteInitialState);
return (
<SiteContext.Provider value={state}><SiteDispatchContext.Provider value={reducer}><View style={styles.container}>
<Text>Open up App.js to start working on your app!</Text>
<List />
</View></SiteDispatchContext.Provider></SiteContext.Provider>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
//list.js
import {useContext, useState} from 'react';
import {StyleSheet, Text, TextInput, View} from 'react-native';
import {SiteContext, SiteDispatchContext} from "./context";
const List = () => {
const siteData = useContext(SiteContext);
const siteDispatch = useContext(SiteDispatchContext);
const [firstName, setFirstName] = useState('');
const end = () => {
siteData.data.user.setFirstName(firstName);
siteDispatch({type: 'update'});
}
let li = [];
const rndInt = Math.floor(Math.random() * 6) + 1;
for(let i= 0 ; i < rndInt; i++){
li.push(<ListItem />);
}
return <View><Text>{siteData.data.userData.firstName + ' ' + siteData.data.userData.lastName}</Text><TextInput onChangeText={(text)=> { setFirstName(text); }} onSubmitEditing={()=>{ end(); }}/>{li}</View>
}
export default List;
const ListItem = () => {
const siteData = useContext(SiteContext);
return <View><Text>{siteData.data.userData.lastName}</Text></View>
}
//context.js
import { createContext } from 'react';
export const SiteContext = createContext(null);
export const SiteDispatchContext = createContext(null);
export const siteReducer = (siteData, action) => {
switch (action.type) {
case 'update': {
let s = {...siteData};
s.data.userData = s.data.user.getData();
return s;
}
}
}
class User {
firstName = 'Robert';
lastName = 'Palmer'
getData = () => {
return {
firstName: this.firstName,
lastName: this.lastName
}
}
setFirstName = (name) => {
this.firstName = name;
}
setLastName = (name) => {
this.lastName = name;
}
}
const user = new User();
export const siteInitialState = {data: {user: user, userData: user.getData(), cart: null, cartData: {}} };