| 导语 梳理父组件向子组件、子组件向父组件、跨级组件间、非嵌套组件间通信方式以及如何用redux进行数据管理
1、父组件向子组件通信
1)传递props
最常见的组件通信方式,一般父组件通过props向子组件传送数据
父组件App.js
import React, { Component } from "react";
import { render } from "react-dom";
import Sub from "./sub";
class App extends Component {
render() {
return <Sub title="hello, react" />;
}
}
render(<App />, document.getElementById("root"));
子组件Sub.js
import React from "react";
const Sub = props => {
return <h1>{props.title}</h1>;
};
export default Sub;
2)调用子组件方法
父组件直接调用子组件的方法,也可以达到通信的目的
父组件App.js
import React, { Component } from "react";
import { render } from "react-dom";
import Sub from "./sub";
class App extends Component {
hideSub() {
this.refs.subComponent.hide();
}
render() {
return (
<div>
<Sub ref="subComponent" />
<button onClick={this.hideSub.bind(this)}>隐藏子组件</button>
</div>
);
}
}
render(<App />, document.getElementById("root"));
子组件Sub.js
import React, { Component } from "react";
export default class Sub extends Component {
state = {
show: true
};
hide() {
this.setState({
show: false
});
}
render() {
return this.state.show ? <h1>hello, react</h1> : null;
}
}
2、子组件向父组件通信
1)回调父组件通过props传递过来的方法
父组件App.js
import React, { Component } from "react";
import { render } from "react-dom";
import Sub from "./sub";
class App extends Component {
state = {
showSubComponent: true
};
showSub() {
this.setState({
showSubComponent: true
});
}
hideSub() {
this.setState({
showSubComponent: false
});
}
render() {
return (
<div>
{
this.state.showSubComponent
? <Sub ref="subComponent" hideSub={this.hideSub.bind(this)} />
: null
}
<button onClick={this.showSub.bind(this)}>显示子组件</button>
</div>
);
}
}
render(<App />, document.getElementById("root"));
子组件Sub.js
import React, { Component } from "react";
export default class Sub extends Component {
render() {
let { hideSub } = this.props;
return (
<div>
hi,react <button onClick={() => hideSub()}>隐藏</button>
</div>
);
}
}
2)自定义事件
event.js
import { EventEmitter } from "events";
export default new EventEmitter();
父组件App.js
import React, { Component } from "react";
import { render } from "react-dom";
import emitter from "./event";
import Sub from "./sub";
const list = ["apple", "banana"];
class App extends Component {
state = {
msg: ""
};
componentDidMount() {
// 声明自定义事件
this.eventEmitter = emitter.on("subClick", msg => {
this.setState({
msg
});
});
}
render() {
return (
<div>
{this.state.msg}
<ul>
{list.map((item, index) => (
<Sub key={index} name={item} />
))}
</ul>
</div>
);
}
}
render(<App />, document.getElementById("root"));
子组件Sub.js
import React, { Component } from "react";
import emitter from "./event";
export default class Sub extends Component {
handleClick() {
emitter.emit("subClick", this.props.name);
}
render() {
return <li onClick={this.handleClick.bind(this)}>{this.props.name}</li>;
}
}
3、跨级组件间通信
1)通过props层层传递,中间的组件也需要添加对应的props属性,传递给下一层
import React, { Component } from "react";
import { render } from "react-dom";
const ThemeButton = props => <button style={{color: props.theme}}>{props.theme}</button>
// 中间组件,将theme传递给ThemeButton
const Toolbar = props => <ThemeButton theme={props.theme}/>
class App extends Component {
render() {
return <Toolbar theme="red"/>;
}
}
render(<App />, document.getElementById("root"));
2)使用context
import React, { Component } from "react";
import { render } from "react-dom";
// 创建一个theme context,默认值为light
const { Provider, Consumer } = React.createContext("light");
const ThemeButton = props => {
return <Consumer>{theme => <button {...props}>{theme}</button>}</Consumer>;
};
// 中间组件
const Toolbar = props => <ThemeButton />;
class App extends Component {
render() {
return (
<Provider value="dark">
<Toolbar />
</Provider>
);
}
}
render(<App />, document.getElementById("root"));
4、非嵌套组件间通信
就是没有包含关系的组件,比如兄弟组件
1)自定义事件
父组件App.js
import React, { Component } from "react";
import { render } from "react-dom";
import Sub from "./sub";
import Sub2 from "./sub2";
class App extends Component {
render() {
return (
<div>
<Sub />
<Sub2 />
</div>
);
}
}
render(<App />, document.getElementById("root"));
子组件Sub.js
import React, { Component } from "react";
import emitter from "./event";
export default class Sub extends Component {
handleClick() {
emitter.emit("siblingsMsg", "组件一");
}
render() {
return (
<div>我是组件一:<button onClick={this.handleClick.bind(this)}>点我点我</button></div>
)
}
}
子组件Sub2.js
import React, { Component } from "react";
import emitter from "./event";
export default class Sub2 extends Component {
state = {
msg: ''
}
componentDidMount() {
// 声明自定义事件
this.eventEmitter = emitter.on("siblingsMsg", msg => {
this.setState({
msg
});
});
}
render() {
return (
<div>我是组件二:收到来自{this.state.msg}的信息</div>
);
}
}
5、使用redux进行数据管理
入口文件index.js
import React, { Component } from "react";
import { render } from "react-dom";
import { createStore, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import { createLogger } from "redux-logger";
import thunk from "redux-thunk";
import rootReducer from "./reducers";
import App from "./components/app";
const middleware = [thunk];
if (process.env.NODE_ENV !== "production") {
middleware.push(createLogger());
}
const store = createStore(rootReducer, applyMiddleware(...middleware));
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
components/app.js
import React, { Component } from "react";
import { connect } from "react-redux";
import Sub from "./sub";
import { changeList, toggleItem } from "../actions";
class App extends Component {
componentWillMount() {
this.props.changeList();
}
render() {
const { list, toggle } = this.props;
return (
<ul>
{list &&
list.map((item, index) => (
<Sub
key={item.id}
{...item}
doClick={() => toggle(item.id)}
/>
))}
</ul>
);
}
}
const mapStateToProps = state => ({
list: state
});
const mapDispatchToProps = dispatch => ({
toggle: id => dispatch(toggleItem(id)),
changeList: () => dispatch(changeList())
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);
components/sub.js
import React, { Component } from "react";
export default class Sub extends Component {
render() {
const { id, name, removed, doClick } = this.props;
return (
<li
onClick={doClick}
style={{ textDecoration: removed ? "line-through" : "none" }}
>
{name}
</li>
);
}
}
actions/actionTypes.js
export const CHANGE_LIST = "CHANGE_LIST";
export const TOGGLE_ITEM = "TOGGLE_ITEM";
actions/index.js
import * as TYPES from "./actionTypes";
// mock data
import _list from "./list.json";
// 获取列表数据
const receiveList = list => ({
type: TYPES.CHANGE_LIST,
list
});
export const changeList = () => dispatch => {
setTimeout(() => {
dispatch(receiveList(_list));
}, 100);
};
//切换子项显示
export const toggleItem = id => ({
type: TYPES.TOGGLE_ITEM,
id
});
list.json
[
{
"id": 1,
"name": "Apple",
"removed": false
},
{
"id": 2,
"name": "Banana",
"removed": false
},
{
"id": 3,
"name": "Orange",
"removed": true
}
]
reducers/index.js
import * as TYPES from "../actions/actionTypes";
const list = (state = [], action) => {
switch (action.type) {
case TYPES.CHANGE_LIST:
return action.list;
case TYPES.TOGGLE_ITEM:
console.log("")
return state.map(item => {
return item.id === action.id ? { ...item, removed: !item.removed } : item;
});
default:
return state;
}
};
export default list;
6、业界其他数据管理方案,例如,mobx
demo使用库的版本如下:
总结
react通信方式有较多选择,需要根据具体的业务选择合适的数据传递方式,当交互较为复杂,共享数据多,引入redux
是明智的决定