zoukankan      html  css  js  c++  java
  • redux

    redux

    记录一下 redux 的一些用法,如果想学习 redux,建议看官方文档,另外推荐一本huzidaha写的react小书,里面讲解了一些 react 和 redux 的原理。

    start


    运行如下命令,不了解 npx 的,可以看一下阮一峰的文章

    // 脚手架创建项目
    npx create-react-app redux-test
    // 进入文件夹
    cd redux-test
    // 启动 react
    npm run start
    

    然后安装 redux:

    npm i -S redux react-redux
    

    接着把 src 下的文件都删掉几个,只剩两个文件:

    src/
    |--index.js
    |--serviceWorker.js
    

    redux and react-redux


    打开上面留下的 index.js,删掉里面的代码,敲下自己的代码,然后刷新网页。

    // index.js
    import React, { Component } from 'react'
    import ReactDOM from 'react-dom'
    import * as serviceWorker from './serviceWorker'
    import { createStore } from 'redux'
    import { connect, Provider  } from 'react-redux'
    
    // actionTypes
    const NUM_ADD = 'NUM_ADD';
    
    // actionCreator
    function addNum () {
      return {
        type: NUM_ADD
      }
    }
    
    // reducer
    const initialState = {
      num: 0
    }
    
    function counter (state = initialState, action) {
      switch  (action.type) {
        case NUM_ADD:
          return {
            num: state.num + 1
          }
        default:
          return state
      }
    }
    
    // store
    const store = createStore(counter)
    
    // App
    class App extends Component {
      render() {
        return (
          <div>
            {this.props.num}
            <button onClick={this.props.onClickAdd}>add</button>
          </div>
        );
      }
    }
    
    // connect(mapStateToProps, mapDispatchToProps)(component)
    const mapStateToProps = state => {
      return {
        num: state.num
      }
    }
    
    const mapDispatchToProps = dispatch => {
      return {
        onClickAdd: () => {
          dispatch(addNum())
        }
      }
    }
    
    App = connect(mapStateToProps, mapDispatchToProps)(App)
    
    // Provider 传入 store
    ReactDOM.render(
    <Provider store={store}>
      <App />
    </Provider>, document.getElementById('root'));
    
    serviceWorker.unregister();
    

    mapDispatchToProps


    connect中的mapDispatchToProps可以是一个函数,也可以是一个对象,函数写法如上。

    mapDispatchToProps是一个对象时,redux 会把它里面的属性作为actionCreator交给dispatch使用,简单来说,就是 redux 帮你把对象里面的属性封装为函数型的mapDispatchToProps,写法如下:

    // index.js
    ...
    // App
    class App extends Component {
      render() {
        return (
          <div>
            {this.props.num}
          	// 这里也进行了修改 
            // onClickAdd -> addNum
            <button onClick={this.props.addNum}>add</button>
          </div>
        );
      }
    }
    ...
    // connect
    App = connect( 
      state => state, 
      { addNum }
    )(App)
    ...
    

    @connect


    connect可以使用装饰器的写法。

    // 装饰器的 babel 插件
    npm i -S @babel/plugin-proposal-decorators
    

    然后进行配置 plugin,这里有两种配置方法:

    1. 使用 create-react-app 的配置

      // 暴露配置
      npm run eject
      // 会进行确认
      Are you sure you want to eject? This action is permanent.(y/n) y
      

      然后会看到文件夹内多了一些东西,打开项目根路径下的 package.json 文件,找到 babel 配置项:

      // package.json
      ...
      "babel": {
          "presets": [
            "react-app"
          ],
          "plugins": [["@babel/plugin-proposal-decorators", { "legacy": true }]]
        },
      ...
      
    2. 使用独立文件配置 babel:

      打开项目根路径下的 package.json 文件,找到 babel 配置项,将他删掉:

      // package.json
      ...
      // 把这个删掉
      "babel": {
          "presets": [
            "react-app"
          ],
        },
      ...
      

      然后在项目根目录创建.babelrc文件。

      // .babelrc
      {
        "presets": ["react-app"],
        "plugins": [
          ["@babel/plugin-proposal-decorators", { "legacy": true }]
      }
      

    配置完 babel 之后,打开 src 下的 index.js 进行修改:

    // index.js
    ...
    // App
    // 装饰器写法
    @connect(
      state => state, 
      { addNum }
    )
    class App extends Component {
      render() {
        return (
          <div>
            {this.props.num}
            <button onClick={this.props.addNum}>add</button>
          </div>
        );
      }
    }
    ...
    

    注意:对于装饰器的支持只是实验性的,未来可能会改动。

    redux-thunk


    可以使用中间件 redux-thunk 进行异步操作,它可以让actionCreator不返回action对象,而是返回一个函数,可以在函数内封装逻辑。

    function incrementIfOdd() {
      // 接收两个参数
      // getState() 可以拿到 store 中的 state
      return (dispatch, getState) => {
        const { counter } = getState();
        if (counter % 2 === 0) {
          return;
        }
        dispatch(increment());
      };
    }
    

    首先还是安装:

    npm i -S redux-thunk
    

    然后打开 index.js 进行修改:

    // index.js
    ...
    import {createStore, applyMiddleware} from 'redux'
    import thunk from 'redux-thunk'
    ...
    // store
    const store = createStore(counter, applyMiddleware(thunk))
    ...
    

    异步操作有很多,这里将 num 的增加推迟为 1s 后才进行:

    // index.js
    // actionCreator
    function addNum () {
      return {
        type: NUM_ADD
      }
    }
    
    function addNumAsync () {
      return dispatch => {
        setTimeout(() => {
          dispatch({type: NUM_ADD})
        }, 1000)
      }
    }
    
    // App
    @connect(
      state => state, 
      { addNum, addNumAsync }
    )
    class App extends Component {
      render() {
        return (
          <div>
            {this.props.num}
            <button onClick={this.props.addNum}>add</button>
            <button onClick={this.props.addNumAsync}>add after 1s</button>
          </div>
        );
      }
    }
    

    combineReducers


    当你有多个 reducer 时,可以使用combineReducers,进行合并。

    import { createStore, combineReducers } from 'redux'
    
    const allReducer = combineReducers({
      reducerOne,
      reducersTwo
    })
    
    store = createStore(allReducer)
    

    bindActionCreators


    把 actionCreator 传到一个子组件中,却不想让这个组件觉察到 Redux 的存在,而且不希望把 dispatch 或 store 传给它时,可以使用bindActionCreators

    这里使用官方文档的例子:

    // TodoActionCreators.js
    export function addTodo(text) {
      return {
        type: 'ADD_TODO',
        text
      };
    }
    
    export function removeTodo(id) {
      return {
        type: 'REMOVE_TODO',
        id
      };
    }
    
    // SomeComponent.js
    import { Component } from 'react';
    import { bindActionCreators } from 'redux';
    import { connect } from 'react-redux';
    
    import * as TodoActionCreators from './TodoActionCreators';
    console.log(TodoActionCreators);
    // {
    //   addTodo: Function,
    //   removeTodo: Function
    // }
    
    class TodoListContainer extends Component {
      constructor(props) { 
        super(props);
    
        const {dispatch} = props;
    
        // 这是一个很好的 bindActionCreators 的使用示例:
        // 你想让你的子组件完全不感知 Redux 的存在。
        // 我们在这里对 action creator 绑定 dispatch 方法,
        // 以便稍后将其传给子组件。
    
        this.boundActionCreators = bindActionCreators(TodoActionCreators, dispatch);
        console.log(this.boundActionCreators);
        // {
        //   addTodo: Function,
        //   removeTodo: Function
        // }
      }
    
      componentDidMount() {
        // 由 react-redux 注入的 dispatch:
        let { dispatch } = this.props;
    
        // 注意:这样是行不通的:
        // TodoActionCreators.addTodo('Use Redux')
    
        // 你只是调用了创建 action 的方法。
        // 你必须要同时 dispatch action。
    
        // 这样做是可行的:
        let action = TodoActionCreators.addTodo('Use Redux');
        dispatch(action);
      }
    
      render() {
        // 由 react-redux 注入的 todos:
        let { todos } = this.props;
    
        return <TodoList todos={todos} {...this.boundActionCreators} />;
    
        // 另一替代 bindActionCreators 的做法是
        // 直接把 dispatch 函数当作 prop 传递给子组件,但这时你的子组件需要
        // 引入 action creator 并且感知它们
    
        // return <TodoList todos={todos} dispatch={dispatch} />;
      }
    }
    
    export default connect(state => ({ todos: state.todos }))(TodoListContainer)
    

    备注


    个人学习 redux 的感受,看,不如动手去敲。

  • 相关阅读:
    Python 列表元素排重uniq
    Python正则表达式汇总
    Python 正则表达式:只要整数和小数
    c++写入txt文件
    OpenMP求完数
    Python分割list
    用ConfigParser模块读写配置文件——Python
    Python 正则表达式
    教程和工具--用wxPython编写GUI程序的
    matlab 之字体调整
  • 原文地址:https://www.cnblogs.com/guolao/p/10571605.html
Copyright © 2011-2022 走看看