梳理父元件向子元件、子元件向父元件、跨級元件間、非巢狀元件間通訊方式以及如何用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
是明智的決定