美文网首页
redux 超神篇

redux 超神篇

作者: 林ze宏 | 来源:发表于2019-07-07 21:12 被阅读0次

    为什么需要redux

    • 解决组件间数据传递的问题

    redux的简单流程原理图:

    例1:简单流程原理图 例2:简单流程原理图

    redux 和 react-redux区别

    redux 和 react-redux区别

    单独使用redux

    npm i redux react-redux

    • index.js(入口文件)
    import React from 'react';
    import ReactDOM from 'react-dom';
    import App from './App';
    import registerServiceWorker from './registerServiceWorker';
    import { createStore } from 'redux';
    import reducer from './reducers/counter';
    
    const store = createStore(reducer); // 创建store,store其实就是reducer集合
    
    // store.subscribe(() => console.log("State updated!", store.getState()));
    
    const render = () => {
      ReactDOM.render(
        <App
          onIncrement={ () => store.dispatch({ type: "INCREMENT" }) } // store通过调用dispatch方法,传递一个action(action就是一个对象)参数,从而触发调用reducer
          onDecrement={ () => store.dispatch({ type: "DECREMENT" }) }
          value={ store.getState() }
        />, document.getElementById('root'));
    };
    
    render();
    
    store.subscribe(render); // store 要监听的函数
    
    registerServiceWorker();
    
    • App.js
    import React, { Component } from 'react';
    import PropTypes from 'prop-types';
    
    class App extends Component {
      render() {
        return (
          <div className="container">
            <h1 className="jumbotron-heading text-center">{ this.props.value }</h1>
            <p className="text-center">
              <button onClick={ this.props.onIncrement } className="btn btn-primary mr-2">Increase</button>
              <button onClick={ this.props.onDecrement } className="btn btn-danger my-2">Decrease</button>
            </p>
          </div>
        );
      }
    }
    
    App.propTypes = {
      value: PropTypes.number.isRequired,
      onIncrement: PropTypes.func.isRequired,
      onDecrement: PropTypes.func.isRequired
    }
    
    export default App;
    

    reducers/counter.js

    const counter = (state = 0, action = {}) => {
      switch(action.type) {
        case 'INCREMENT':
          return state + 1;
        case 'DECREMENT':
          return state - 1;
        default: return state;
      }
    }
    
    export default counter;
    

    react+redux

    npm i react-redux

    • index.js
    import React from 'react';
    import ReactDOM from 'react-dom';
    import App from './App';
    import registerServiceWorker from './registerServiceWorker';
    import { createStore } from 'redux';
    import reducer from './reducers/counter';
    import { Provider } from 'react-redux';
    
    const store = createStore(reducer);
    
    // store.subscribe(() => console.log("State updated!", store.getState()));
    
    ReactDOM.render(
      <Provider store={ store }> // 通过Provider注入store
        <App />
      </Provider>,
      document.getElementById('root')
    );
    
    registerServiceWorker();
    
    • constants/index.js(定义常量)
    export const INCREMENT = "INCREMENT";
    export const DECREMENT = "DECREMENT";
    

    actions/index.js(抽离定义actions)

    import { INCREMENT, DECREMENT } from '../constants';
    
    export const increment = () => {
      return {
        type: INCREMENT
      }
    };
    
    export const decrement = () => {
      return {
        type: DECREMENT
      }
    };
    
    • App.js
    import React, { Component } from 'react';
    import { connect } from 'react-redux';
    
    class App extends Component {
      render() {
        return (
          <div className="container">
            <h1 className="jumbotron-heading text-center">{ this.props.counter }</h1>
            <p className="text-center">
              <button className="btn btn-primary mr-2">Increase</button>
              <button className="btn btn-danger my-2">Decrease</button>
            </p>
          </div>
        );
      }
    }
    
    const mapStateToProps = (state) => { // state参数,相当于 store.getState(),把state转成props
      return {
        counter: state
      };
    };
    
    export default connect(mapStateToProps)(App); // 通过connect,引入store,通过第一个参数mapStateToProps,注入state。也就是说把state传递给我component。
    
    

    使用combineReducers组合多个reducer

    • reducers/user.js(新建user reducer)
    const user = (state = "redux111", action = {}) => {
      switch(action.type) {
        default: return state;
      }
    }
    
    export default user;
    
    
    • reducers/index.js(新建index reducer,使用combineReducers合并导入的各个reducer)
    import { combineReducers } from 'redux';
    
    import counter from './counter';
    
    import user from './user';
    
    const rootReducer = combineReducers({
      counter,
      user
    });
    
    export default rootReducer;
    

    index.js

    import React from 'react';
    import ReactDOM from 'react-dom';
    import App from './App';
    import registerServiceWorker from './registerServiceWorker';
    import { createStore } from 'redux';
    import rootReducer from './reducers'; // !!!使用的是rootReducer
    import { Provider } from 'react-redux';
    
    const store = createStore(rootReducer);
    
    // store.subscribe(() => console.log("State updated!", store.getState()));
    
    ReactDOM.render(
      <Provider store={ store }>
        <App />
      </Provider>,
      document.getElementById('root')
    );
    
    registerServiceWorker();
    
    
    • App.js
    import React, { Component } from 'react';
    import { connect } from 'react-redux';
    import PropTypes from 'prop-types';
    
    class App extends Component {
      render() {
        return (
          <div className="container">
            <h1 className="jumbotron-heading text-center">{ this.props.counter }</h1>
            <p className="text-center">
              <button className="btn btn-primary mr-2">Increase</button>
              <button className="btn btn-danger my-2">Decrease</button>
            </p>
          </div>
        );
      }
    }
    
    const mapStateToProps = (state) => {
    // console.dir(state);
      return {
        counter: state.counter // 使用的时候要先获取对应的key,console就知道了。
      };
    };
    
    App.propTypes = {
      counter: PropTypes.number.isRequired
    }
    
    export default connect(mapStateToProps)(App);
    
    

    component获取dispatch的几种方法

    方法一:connect没有传递第二个参数,直接在component的render方法中获取

    • App.js
    import React, { Component } from 'react';
    import { connect } from 'react-redux';
    import PropTypes from 'prop-types';
    import { increment } from './actions'; // 这里获取的函数,调用返回一个对象
    
    class App extends Component {
      render() {
        const { dispatch } = this.props; // 方法一:直接在props中获取dispatch函数
        return (
          <div className="container">
            <h1 className="jumbotron-heading text-center">{ this.props.counter }</h1>
            <p className="text-center">
    // 同时给action传递参数
              <button onClick={ () => dispatch(increment({id: 1, name: "hfpp2012"})) } className="btn btn-primary mr-2">Increase</button>
              <button className="btn btn-danger my-2">Decrease</button>
            </p>
          </div>
        );
      }
    }
    
    const mapStateToProps = (state) => {
      return {
        counter: state.counter
      };
    };
    
    App.propTypes = {
      counter: PropTypes.number.isRequired
    }
    
    export default connect(mapStateToProps)(App); // 没有传递第二个参数
    
    
    • actions/index.js(获取参数)
    export const increment= (obj) => {
      return {
        type: INCREMENT,
        obj
      }
    };
    
    
    • reducers/counter.js(使用参数)
    const counter = (state = 1, action = {}) => {
      switch (action.type) {
        case 'INCREMENT':
          console.dir(action.obj); // !!!
          return state + 1;
        case 'DECREMENT':
          return state - 1;
        default: return state;
      }
    }
    
    export default counter;
    
    

    方法二:connect传递第二个参数

    • App.js
    import React, { Component } from 'react';
    import { connect } from 'react-redux';
    import PropTypes from 'prop-types';
    import { increment } from './actions';
    
    class App extends Component {
      render() {
        const { aa } = this.props; // 自定义的key,aa其实就是一个函数,可以传递参数
        return (
          <div className="container">
            <h1 className="jumbotron-heading text-center">{ this.props.counter }</h1>
            <p className="text-center">
              <button onClick={ () => aa('dsdsd') } className="btn btn-primary mr-2">Increase</button>
              <button className="btn btn-danger my-2">Decrease</button>
            </p>
          </div>
        );
      }
    }
    
    const mapStateToProps = (state) => {
      return {
        counter: state.counter
      };
    };
    
    const mapDispatchToProps = (dispatch) => {
      return {
        aa: (name) => { dispatch(increment(name)) }
      }
    };
    
    App.propTypes = {
      counter: PropTypes.number.isRequired
    }
    
    export default connect(mapStateToProps, mapDispatchToProps)(App); // 传递第二个参数
    
    

    方法三:使用bindActionCreators

    • App.js
    import React, { Component } from 'react';
    import { connect } from 'react-redux';
    import PropTypes from 'prop-types';
    import { increment, decrement } from './actions';
    import { bindActionCreators } from 'redux';
    
    class App extends Component {
      render() {
        const { increment, decrement } = this.props;
        return (
          <div className="container">
            <h1 className="jumbotron-heading text-center">{this.props.counter}</h1>
            <p className="text-center">
              <button onClick={() => increment()} className="btn btn-primary mr-2">Increase</button>
              <button onClick={() => decrement()} className="btn btn-danger my-2">Decrease</button>
            </p>
          </div>
        );
      }
    }
    
    const mapStateToProps = (state) => {
      return {
        counter: state.counter
      };
    };
    
     const mapDispatchToProps = (dispatch) => {
    //  bindActionCreators的好处是如果需要传递参数的时候,不用写。关于它的说明,可以在官网查看 
    //  http://www.redux.org.cn/docs/api/bindActionCreators.html
    // 方式一写法:
    // return {
    //   increment: bindActionCreators(increment, dispatch),
    //   decrement: bindActionCreators(decrement, dispatch)
    // }
    // 方式二写法:
       return bindActionCreators({ increment, decrement }, dispatch);
    };
    
    App.propTypes = {
      counter: PropTypes.number.isRequired,
      increment: PropTypes.func.isRequired,
      decrement: PropTypes.func.isRequired
    }
    // 第一、二种方式:
    export default connect(mapStateToProps, mapDispatchToProps)(App);
    
    

    方法四:直接传,这种方式用得比较多

    • App.js
    
    import React, { Component } from 'react';
    import { connect } from 'react-redux';
    import PropTypes from 'prop-types';
    import { increment, decrement } from './actions';
    
    class App extends Component {
      render() {
        const { increment, decrement } = this.props;
        return (
          <div className="container">
            <h1 className="jumbotron-heading text-center">{this.props.counter}</h1>
            <p className="text-center">
              <button onClick={() => increment()} className="btn btn-primary mr-2">Increase</button>
              <button onClick={() => decrement()} className="btn btn-danger my-2">Decrease</button>
            </p>
          </div>
        );
      }
    }
    
    const mapStateToProps = (state) => {
      return {
        counter: state.counter
      };
    };
    
    App.propTypes = {
      counter: PropTypes.number.isRequired,
      increment: PropTypes.func.isRequired,
      decrement: PropTypes.func.isRequired
    }
    export default connect(mapStateToProps, { increment, decrement })(App); // 在第二个参数,直接传对应的action函数
    
    

    方法四改进,如果有很多action的时候,那就麻烦了

    • App.js
    import React, { Component } from 'react';
    import { connect } from 'react-redux';
    import PropTypes from 'prop-types';
    import * as types from './actions'; // 很多action的时候,把所有的导出
    import { bindActionCreators } from 'redux';
    
    class App extends Component {
      render() {
        const { increment, decrement } = this.props; // 这里同样可以直接拿到所有的action
        return (
          <div className="container">
            <h1 className="jumbotron-heading text-center">{this.props.counter}</h1>
            <p className="text-center">
              <button onClick={() => increment()} className="btn btn-primary mr-2">Increase</button>
              <button onClick={() => decrement()} className="btn btn-danger my-2">Decrease</button>
            </p>
          </div>
        );
      }
    }
    
    const mapStateToProps = (state) => {
      return {
        counter: state.counter
      };
    };
    
     const mapDispatchToProps = (dispatch) => {
       return bindActionCreators(types , dispatch); // 这里直接传递types
     };
    
    App.propTypes = {
      counter: PropTypes.number.isRequired,
      increment: PropTypes.func.isRequired,
      decrement: PropTypes.func.isRequired
    }
    
    export default connect(mapStateToProps, mapDispatchToProps)(App);
    
    

    使用@connect装饰器

    由于create-react-app并不支持es6 @ 装饰器的语法,这里有可以babel解决,也可以使用custom-react-scripts这个库替换create-react-app原来的核心库react-scripts

    官网:
    https://github.com/kitze/custom-react-scripts
    
    安装替换:
    npm uninstall --save react-scripts;
    npm install --save custom-react-scripts;
    
    使用:
    在项目根目录添加  .env 配置文件。
    支持@装饰器,使用babel的为配置:REACT_APP_DECORATORS=true
    
    其他常见的配置见官网。
    
    

    提示:传递state的时候,不要把整个对象都传递到component,因为component会频繁的更新,有性能问题

    不好的:不要把整个对象user传过来
    @connect(state => ({ 
       user: state.user,
       messages: state.messages
    }))
    
    好的:用到什么传什么
    @connect(state => ({ 
       user_name: state.user.name,
       last_message: state.messages[state.messages.length-1]
    }))
    
    
    • App.js
    import React, { Component } from 'react';
    import { connect } from 'react-redux';
    import PropTypes from 'prop-types';
    import * as types from './actions';
    import { bindActionCreators } from 'redux';
    
    const mapStateToProps = (state) => {
      return {
        counter: state.counter
      };
    };
    
    const mapDispatchToProps = (dispatch) => {
      return bindActionCreators(types, dispatch);
    };
    
    @connect(mapStateToProps, mapDispatchToProps)
    class App extends Component {
      static propTypes = {
        counter: PropTypes.number.isRequired,
        increment: PropTypes.func.isRequired,
        decrement: PropTypes.func.isRequired
      };
    
      render() {
        const { increment, decrement } = this.props;
        return (
          <div className="container">
            <h1 className="jumbotron-heading text-center">{ this.props.counter }</h1>
            <p className="text-center">
              <button onClick={ () => increment() } className="btn btn-primary mr-2">Increase</button>
              <button onClick={ () => decrement() } className="btn btn-danger my-2">Decrease</button>
            </p>
          </div>
        );
      }
    }
    
    export default App;
    
    

    中间件 Middleware(是在action和reducer中间)

    中间件
    • index.js
    import React from 'react';
    import ReactDOM from 'react-dom';
    import App from './App';
    import registerServiceWorker from './registerServiceWorker';
    import { createStore, applyMiddleware } from 'redux'; // 通过 applyMiddleware 
    import rootReducer from './reducers';
    import { Provider } from 'react-redux';
    
    const logger = store => next => action => {
      console.log('dispatching', action);
      let result = next(action); // 表示去执行下一个中间件
      console.log('next state', store.getState());
      return result;
    };
    
    const error = store => next => action => {
      try {
        next(action) // 表示去执行下一个中间件,如果没有下一个中间件了,就去执行reducer。
      } catch(e) {
        console.log('error ' + e);
      }
    };
    
    // 多个箭头函数的理解
    // const logger = function(store) {
    //   return function(next) {
    //     return function(action) {
    //       console.log('dispatching', action);
    //       let result = next(action);
    //       console.log('next state', store.getState());
    //       return result;
    //     }
    //   }
    // }
    
    const store = createStore(rootReducer, {}, applyMiddleware(logger, error));
    
    // store.subscribe(() => console.log("State updated!", store.getState()));
    
    ReactDOM.render(
      <Provider store={ store }>
        <App />
      </Provider>,
      document.getElementById('root')
    );
    
    registerServiceWorker();
    
    
    • reducers/counter.js
    const counter = (state = 1, action = {}) => {
      switch(action.type) {
        case 'INCREMENT':
          throw new Error('error in INCREMENT') // 仅仅为了说明中间件执行顺序
          // return state + 1;
        case 'DECREMENT':
          return state - 1;
        default: return state;
      }
    }
    
    export default counter;
    
    
    结果

    中间件 redux-logger 打印日志

    npm i --save redux-logger

    使用:
    import logger from 'redux-logger';
    
    const store = createStore(rootReducer, {}, applyMiddleware(logger));
    
    

    中间件 redux-thunk 处理异步

    解决action返回函数报错问题。

    • index.js
    import thunk from 'redux-thunk';
    
    const store = createStore(rootReducer, {}, applyMiddleware(logger, thunk));
    
    
    • actions/index.js
    import { INCREMENT, DECREMENT } from '../constants';
    
    export const increment = () => { 
      return dispatch => { // dispatch这个是形参,参数
        setTimeout(() => {
          dispatch({ // 这个是调用reducer
            type: INCREMENT
          });
        }, 2000);
      };
    };
    
    

    ajax

    只处理请求成功后的

    • components/User.js
    import React, { Component } from 'react';
    import { connect } from 'react-redux';
    import { get_user } from '../actions';
    
    class User extends Component {
      render() {
        const { get_user, user } = this.props;
        return (
          <div>
            <h1 className="jumbotron-heading text-center">{ user.email }</h1>
            <p className="text-center">
              <button onClick={ () => get_user() } className="btn btn-success mr-2">GET RANDOM USER</button>
            </p>
          </div>
        );
      }
    }
    
    const mapStateToProps = (state) => {
      return {
        user: state.user
      };
    };
    
    export default connect(mapStateToProps, { get_user })(User);
    
    
    • actions/index.js
      npm i axios
    import axios from 'axios';
    import { FETCH_USER_SUCCESS } from '../constants';
    
    export const get_user = () => {
      return dispatch => {
        axios.get("https://randomuser.me/api/")
          .then(res => {
            // dispatch触发reducer,调用fetch_user(res.data.results[0])返回action对象
            dispatch(fetch_user(res.data.results[0])); 
          })
          .catch(error => {
            console.log(error);
          })
      };
    };
    
    export const fetch_user = (user) => {
      return {
        type: FETCH_USER_SUCCESS,
        user
      }
    };
    
    
    • reducers/user.js
    import { FETCH_USER_SUCCESS } from '../constants';
    
    const user = (state = {}, action = {}) => {
      switch(action.type) {
        case FETCH_USER_SUCCESS:
          return action.user
        default: return state;
      }
    }
    
    export default user;
    
    

    进阶:处理失败和正在请求的情况

    • components/User.js
    import React, { Component } from 'react';
    import { connect } from 'react-redux';
    import { get_user } from '../actions';
    
    class User extends Component {
      render() {
        const { get_user } = this.props;
    
      // 这里获取的是通过mapStateToProps,获取的user对象 
        const { error, isFetching, user } = this.props.user1;
    
        let data;
    
        if (error) {
          data = error;
        } else if (isFetching) {
          data = "Loading...";
        } else {
          data = user.email;
        }
    
        return (
          <div>
            <h1 className="jumbotron-heading text-center">{ data }</h1>
            <p className="text-center">
              <button onClick={ () => get_user() } className="btn btn-success mr-2">GET RANDOM USER</button>
            </p>
          </div>
        );
      }
    }
    
    const mapStateToProps = (state) => {
      return {
        user1: state.user
      };
    };
    
    export default connect(mapStateToProps, { get_user })(User);
    
    
    • actions/index.js
    import axios from 'axios';
    import { FETCH_USER_SUCCESS, FETCH_USER_REQUEST, FETCH_USER_FAILURE } from '../constants';
    
    export const get_user = () => {
      return dispatch => {
        dispatch(fetch_user_request()) // ajax触发之前,正在加载中...
        axios.get("https://randomuser.me/api/")
          .then(res => {
            dispatch(fetch_user(res.data.results[0])); // 成功
          })
          .catch(error => {
            dispatch(fetch_user_failure(error.response.data)); // 失败
          })
      };
    };
    
    export const fetch_user_failure = (error) => {
      return {
        type: FETCH_USER_FAILURE,
        error
      };
    };
    
    export const fetch_user = (user) => {
      return {
        type: FETCH_USER_SUCCESS,
        user
      }
    };
    
    export const fetch_user_request = () => {
      return {
        type: FETCH_USER_REQUEST
      }
    };
    
    
    • reducers/user.js
    import { FETCH_USER_SUCCESS, FETCH_USER_REQUEST, FETCH_USER_FAILURE } from '../constants';
    
    const initialState = {
      isFetching: false,
      error: null,
      user: {}
    };
    // state 初始化为对象
    const user = (state = initialState, action = {}) => {
      switch(action.type) {
        case FETCH_USER_SUCCESS:
          return {
            isFetching: false,
            error: null,
            user: action.user
          };
        case FETCH_USER_REQUEST:
          return {
            isFetching: true,
            error: null,
            user: {}
          }
        case FETCH_USER_FAILURE:
          return {
            isFetching: false,
            error: action.error,
            user: {}
          };
        default: return state;
      }
    }
    
    export default user;
    
    
    • constants/index.js
    export const INCREMENT = "INCREMENT";
    export const DECREMENT = "DECREMENT";
    
    // user
    export const FETCH_USER_SUCCESS = "FETCH_USER_SUCCESS";
    export const FETCH_USER_REQUEST = "FETCH_USER_REQUEST";
    export const FETCH_USER_FAILURE = "FETCH_USER_FAILURE";
    
    

    使用redux-promise-middleware简化action,该插件会衍生出几种状态的action

    官网:https://github.com/pburtchaell/redux-promise-middleware/blob/master/docs/introduction.md
    npm i redux-promise-middleware -s

    • index.js
    import { createStore, applyMiddleware } from 'redux';
    import thunk from 'redux-thunk';
    import promise from 'redux-promise-middleware';
    import rootReducer from '../reducers';
    
      const store = createStore( rootReducer, {}, applyMiddleware(thunk, promise()));
    
    
    • actions/index.js
    import axios from 'axios';
    import { LOAD_USER } from '../constants';
    
    // 通过 redux-promise-middleware 插件,返回
    export const get_user = () => {
      return {
        type: LOAD_USER,
        // payload: axios.get("https://randomuser.me/api/") 
        payload: {
          promise: axios.get("https://randomuser.me/api/")
        }
      };
    };
    
    
    • reducers/user.js
    import { LOAD_USER_FULFILLED, LOAD_USER_PENDING, LOAD_USER_REJECTED } from '../constants';
    
    const initialState = {
      isFetching: false,
      error: null,
      user: {}
    };
    
    const user = (state = initialState, action = {}) => {
      switch(action.type) {
        case LOAD_USER_FULFILLED:
          return {
            isFetching: false,
            error: null,
            user: action.payload.data.results[0]
          };
        case LOAD_USER_PENDING:
          return {
            isFetching: true,
            error: null,
            user: {}
          }
        case LOAD_USER_REJECTED:
          return {
            isFetching: false,
            error: action.payload.response.data,
            user: {}
          };
        default: return state;
      }
    }
    
    export default user;
    
    

    调试插件

    npm i redux-devtools-extension -D

    import { composeWithDevTools } from 'redux-devtools-extension';
    
    const store = createStore(rootReducer, {}, composeWithDevTools(applyMiddleware(logger, thunk, promise())));
    
    // 说明:composeWithDevTools直接包围中间件即可,详见官网
    

    开发和生产环境加载不同的store配置文件

    • index.js
    import React from 'react';
    import ReactDOM from 'react-dom';
    import App from './App';
    import registerServiceWorker from './registerServiceWorker';
    
    import { Provider } from 'react-redux';
    
    import configureStore from './store/configureStore'; // 获取store配置文件
    
    const store = configureStore(); // 函数调用
    
    // store.subscribe(() => console.log("State updated!", store.getState()));
    
    ReactDOM.render(
      <Provider store={ store }>
        <App />
      </Provider>,
      document.getElementById('root')
    );
    
    registerServiceWorker();
    
    
    • store/configureStore.js
    if (process.env.NODE_ENV === 'production') {
      module.exports = require('./configureStore.prod');
    } else {
      module.exports = require('./configureStore.dev');
    }
    
    说明:根据 process.env.NODE_ENV 区分加载哪个配置文件;通过module.exports导出,require获取相应内容。
    
    
    • store/configureStore.dev.js
    import { createStore, applyMiddleware } from 'redux';
    import logger from 'redux-logger';
    import thunk from 'redux-thunk';
    import promise from 'redux-promise-middleware';
    import { composeWithDevTools } from 'redux-devtools-extension';
    import rootReducer from '../reducers';
    
    const configureStore = (preloadedState) => {
      const store = createStore(
        rootReducer,
        preloadedState,
        composeWithDevTools(applyMiddleware(logger, thunk, promise()))
      );
    
      return store;
    };
    
    export default configureStore; // 导出是一个函数,则需要调用后,才返回结果store。
    
    说明:开发环境需要logger和调试插件redux-devtools-extension
    
    
    
    • store/configureStore.prod.js
    import { createStore, applyMiddleware } from 'redux';
    import thunk from 'redux-thunk';
    import promise from 'redux-promise-middleware';
    import rootReducer from '../reducers';
    
    const configureStore = (preloadedState) => {
      const store = createStore(
        rootReducer,
        preloadedState,
        applyMiddleware(thunk, promise())
      );
    
      return store;
    };
    
    export default configureStore;
    
    // 说明:生产环境不需要logger和调试插件。
    
    

    create-react-app redux hmr (热模块加载)

    热模块加载(或者说是热更新)就是当你在开发环境修改代码后,不用刷新整个页面即可看到修改后的效果。

    解决:详见
    https://github.com/facebook/create-react-app/issues/2317

    • index.js
    import React from 'react';
    import ReactDOM from 'react-dom';
    import App from './App';
    import registerServiceWorker from './registerServiceWorker';
    
    import { Provider } from 'react-redux';
    
    import configureStore from './store/configureStore';
    
    const store = configureStore();
    
    // store.subscribe(() => console.log("State updated!", store.getState()));
    
    ReactDOM.render(
      <Provider store={ store }>
        <App />
      </Provider>,
      document.getElementById('root')
    );
    
    if (module.hot) {
      module.hot.accept('./App', () => { // 注意根据实际项目的路径,跟import App from './App' 一致
        ReactDOM.render(
          <Provider store={ store }>
            <App />
          </Provider>,
          document.getElementById('root')
        );
      })
    }
    
    registerServiceWorker();
    
    
    • configureStore.dev.js(开发环境的store配置)
    import { createStore, applyMiddleware } from 'redux';
    import logger from 'redux-logger';
    import thunk from 'redux-thunk';
    import promise from 'redux-promise-middleware';
    import { composeWithDevTools } from 'redux-devtools-extension';
    import rootReducer from '../reducers';
    
    const configureStore = (preloadedState) => {
      const store = createStore(
        rootReducer,
        preloadedState,
        composeWithDevTools(applyMiddleware(logger, thunk, promise()))
      );
    
      if (process.env.NODE_ENV !== "production") {
        if (module.hot) {
          module.hot.accept('../reducers', () => { // 注意这里的reducers路径,跟 import rootReducer from '../reducers'  一致
            store.replaceReducer(rootReducer)
          })
        }
      }
    
      return store;
    };
    
    export default configureStore;
    
    

    相关文章

      网友评论

          本文标题:redux 超神篇

          本文链接:https://www.haomeiwen.com/subject/tabouftx.html