• 前端(十二):react-redux实现逻辑


    一、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
    }
    }
    // 使用getChildContextcontext传递数据
    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.js
    import React from 'react'
    import PropTypes from 'prop-types'

    // 1.仿写redux
    export 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.js
    import 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}) // 以对象的方式获取state
    class 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.js
    import 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')
    );

      其大致流程图如下:

      

  • 相关阅读:
    PyQt5复杂控件(树控件、选项卡控件(滚动条控件、多文档控件、停靠控件)
    PyQt5单元格操作大全
    PyQt5打印机
    PyQt5剪切板操作
    PyQt5的菜单栏、工具栏和状态栏
    PyQt5日历控件及相关操作
    PyQt5控件支持拖拽方法
    《Lua程序设计》第3章 表达式 学习笔记
    Lua中的table构造式(table constructor)
    《Lua程序设计》第2章 类型与值 学习笔记
  • 原文地址:https://www.cnblogs.com/kuaizifeng/p/9425406.html
Copyright © 2020-2023  润新知