一、context实现数据传递
在react中,props和state都可以设置数据。不同的是,props借助组件属性传递数据但不可以渲染组件,它相对来说是“静态的”;state可以监听事件来修改数据并重新渲染组件,它相对来说是“动态的”。要想实现组件间传递数据并且可以根据state重新渲染组件,就必须一层层地传递props和this.state。
redux借助context这个全局API,以及内置的getChildContext方法简化了数据传递。context是一个全局的变量,getChildContext让react有能力向context中写入数据,并让当前组件的所有子组件能够从中获取到数据。
如果在context中传递state状态,那么context的数据就会根据state状态的变化而变化,子组件也会跟着被重新渲染。
import React from ‘react‘import PropTypes from ‘prop-types‘ class SubOneChild extends React.Component{ static contextTypes = { number: PropTypes.number }; render(){ return <h2> { this.context.number } </h2> }} class SubOne extends React.Component{ static contextTypes = { number: PropTypes.number }; render(){ console.log(this.context); return ( <div> <h4> SubOne: { this.context.number }</h4> <SubOneChild /> </div> ) }} class App extends React.Component{ // 使用context必须要对数据类型进行校验 static childContextTypes = { number: PropTypes.number }; constructor(props){ super(props); this.state = { number: 10 } } // 使用getChildContext向context传递数据 getChildContext(){ return this.state } handleChange(){ this.setState({ number: this.state.number + 1 }) } render(){ return ( <div> <h2>App: { this.state.number }</h2> <button onClick={()=>this.handleChange()}>点击增加</button> <SubOne /> </div> ) }}export default App
上例并没有使用props传递this.state.number,而是通过context实现了数据的传递。它将数据传递简化为两步:第一步,父组件向context注入state数据并规定context数据协议,第二部,任意层级的子组件根据同样的context数据协议获取数据。
二、实现同步react-redux
react-redux做了两步:将整个ReactDOM要渲染的组件用Provider包裹起来,只用Provider向context注入state数据;任意组件通过connect从props中获取state数据和sction事件。
// src/my-react-redux.jsimport React from ‘react‘import PropTypes from ‘prop-types‘ // 1.仿写reduxexport function createStore(reducer) { let currentState = {}; let currentListeners = []; function getState() { return currentState; } function subscribe(listener) { currentListeners.push(listener); } function dispatch(action) { currentState = reducer(currentState, action); currentListeners.forEach(v=>v()) } dispatch({type: ‘@#$%^&*(‘}); // 执行一遍获取默认state return { getState, subscribe, dispatch }} // 2.仿写react-redux // Provider: 把store写到context里,全局只包裹一次,这样所有的子元素都可以取到store export class Provider extends React.Component{ // 规定数据协议 static childContextTypes = { store: PropTypes.object }; // 从Provider属性中获取过来store constructor(props, context){ super(props, context); this.store = props.store; } // 向context中传递当前组件的store,以允许任意子组件都可以获取这个store getChildContext(){ return { store: this.store } } // 返回被包裹的组件 render(){ return this.props.children }} // connect接收组件,更新redux数据放到组件的属性里 // export function connect(mapStateToProps, mapDispatchToProps) {// return function (WrapComponent) {// return class ConnectComponent extends React.Component{}// }// } // 上述带参数的装饰器的简便写法export const connect = (mapStateToProps=state=>state, mapDispatchToProps={})=>(WrapComponent)=>{ return class ConnectComponent extends React.Component{ // 规定context数据协议 static contextTypes = { store: PropTypes.object }; // 注意要继承context,在state中设置props参数来接收最外层父组件向context中传递的数据 constructor(props, context){ super(props, context); this.state = { props: {} } } // 获取store,并调用store的监听函数,来监听一个事件;监听后要执行一次更新来渲染一次组件 componentDidMount(){ const { store } = this.context; store.subscribe(()=>this.update()); // 监听数据变化 this.update(); } // 主函数,主要调用store.dispatch(action)方法来更新store以及中间组件ConnectComponent的状态ConnectComponentConCthis.state.props update(){ const { store } = this.context; const stateProps = mapStateToProps(store.getState()); const dispatchProps = ConnectComponent.bindActionCreator(mapDispatchToProps, store.dispatch); this.setState({ // 因为props会被重写,一定要注意这里的顺序 props: { ...this.state.props, ...stateProps, ...dispatchProps } }) } static bindActionCreator(mapDispatchToProps, dispatch){ let event = {}; Object.keys(mapDispatchToProps).forEach(v=>{ let func = mapDispatchToProps[v]; event[v] = (...args)=>dispatch(func(...args)); // 注意把func的参数传进来,用dispatch调用func }); return event } render(){// 将中间组件ConnectComponent的state透传给被包裹的组件 return <WrapComponent { ...this.state.props } /> } }};
把reducer重写一下:
// src/my-reducer.js const ADD = ‘ADD‘;const REDUCE = ‘REDUCE‘;export function reducer(state=0, action) { // action都是事件类型 switch(action.type){ case ADD: return state + 1; case REDUCE: return state - 1; default: return 10 }}export function add() { return { type: ADD }}export function reduce() { return { type: REDUCE }}
app.js中内容改写为:
// src/my-app.jsimport React from ‘react‘import { connect } from "./my-react-redux";import { add, reduce } from "./my-reducer"; @connect(state=>({number:state}))class SubOneChild extends React.Component{ render(){ return <p> { this.props.number } </p> }}@connect(state=>({number:state}))class SubOne extends React.Component{ render(){ return ( <div> <h3> SubOne: { this.props.number }</h3> <SubOneChild /> </div> ) }}@connect(state=>({number:state}), { add, reduce}) // 以对象的方式获取stateclass App extends React.Component{ render(){ // console.log(this.props); return ( <div> <h1>App: { this.props.number }</h1> <button onClick={this.props.add}>点击增加</button> <SubOne /> </div> ) }}export default App
在index.js中引入App。
// src/index.jsimport React from ‘react‘import ReactDOM from ‘react-dom‘import { reducer } from ‘./my-reducer‘import {createStore, Provider} from ‘./my-react-redux‘import App from ‘./my-app‘ const store = createStore(reducer);ReactDOM.render( <Provider store={ store }> <App /> </Provider>, document.getElementById(‘root‘));
其大致流程图如下:
原文地址:https://www.cnblogs.com/kuaizifeng/p/9425406.html
时间: 2024-11-08 19:37:55