React Redux

Redux 基础知识

Redux 原则

  • 唯一数据源:Redux 应用只维护一个全局状态对象;
  • 状态只读:不能直接修改应用的状态,必须通过 action 来实现修改;
  • 状态改变通过纯函数完成:reducer 必须是一个纯函数,接收到 action 后也不能直接修改状态,而是要返回一个新的状态,且 reducer 不能有副作用(比如:实时获取时间戳)

Redux 组成

redux 主要由 3 个部分组成:action、reducer、store,一张图揭示它们之间的关系:

action

action 是 store 唯一的数据来源,通过dispatch方法传递给 store。

actionCreator

action 是一个对象,由 type 和 data 属性组成,一般会定义一个创建 action 对象的函数并暴露出去,统一写在 actionCreators.js 内:

// actionCreators.js
export const getPostList = data =>({
    type:'post/get_post_list',
    data
})

actionTypes

一般 action 中的 type 属性会用一个专门的文件 actionTypes.js 来管理,防止 action 和 reducer 中的 type 属性不一致,例如:

// actionTypes.js
export const GET_POST_LIST = "post/get_post_list";
export const REMOVE_FIRST_ITEM = "post/remove_first_item";
export const REMOVE_LAST_ITEM = "post/remove_last_item";

那么上面的 getPostList 就可以修改为:

import { GET_POST_LIST } from './actionTypes.js';

export const getPostList = data =>({
    type: GET_POST_LIST,
    data
})

dispatch

通知 store 需要用到 dispatch方法,dispatch 可以接收 action 对象,接收后又传给了 reducer,这个过程是同步的,如果我们想要异步处理,比如:从服务器异步请求数据,单纯的 redux 是不行的,需要用到 redux-thunk 插件。

同步 dispatch:

// App.jsx
import { getPostList } from './actionCreators.js';

class App extends Component{
    const handleClick=()=>{
        this.store.dispatch( getPostList( {id:'123'} ) );
    }
    
    render(){
        return <div onClick={handleClick}></div>
    }
}

异步 dispatch:

// App.jsx
import {demoActionFunc} from 'actionTypes';

class App extends Component{
    const handleClick=()=>{
        this.store.dispatch( postFilter() ); 
    }
    
    render(){
        return <div onClick={handleClick}></div>
    }
}

// actionCreators.js
export const getPostList = data =>({
    type:'post/get_post_list',
    data
})

export const postFilter = () =>{
    return dispatch =>{
        // 先运行AJAX请求,后执行dispatch
        ajax().then(( res )=>{
            dispatch( getPostList(res) )
        });
    }
}

reducer

reducer 用于对传入的 action 根据其 type 值作出响应,然后返回一个新的 state 对象,举个例子:

import * as actionTypes from "./actionTypes";

const defaultState = {
  page: 0,
  perPage: 10
};

export default (state = defaultState, action) => {
    switch(action.type){
        case 'demoActionType':
            return ...
        default:
            return state
    }
}

一个项目中会有多个 reducer,一般常用 combineReducer 将他们汇总起来,一并传入 createStore 方法内,用于创建 store:

import { combineReducers } from 'redux';

const reducers = combineReducers({ reducer1, reducer2 });

store

store是连接 action 和 reducer 的中间人,一个应用只会存在一个 store,保存着应用的状态。

store 通过createStore(reducers,initalStore) 方法生成,下面是包含 redux-thunk 的例子:

import { createStore, compose, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import reducers from "./reducers";
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

const store = createStore(reducer, composeEnhancers(applyMiddleware(thunk)));

export default store;

store 的 3 个 API:

  • .getState()

    用来访问 store 中的状态 state

  • .dispatch(action)

    用来发送更新状态意图

  • .subscribe(listenerFunction)

    用来监听状态的变化

    function handleChange() {
        // do something
    }
    
    const unsubscribe = store.subscribe(handleChange)
    unsubscribe()
    

使用示例

import { connect, bindActionCreators } from "react-redux";

// 第二个可选参数ownProps代表组件本身的props,如果传入,则当props变化时,mapStateToPorps就会被调用
// 适合state与props有关联的时候使用
const mapStateToProps = (state, ownProps) => {
  return {
    todos: state.todos
  }
}

const mapDispatchToProps = (dispatch, ownProps) => {
  return bindActionCreators({
    increase: action.increase,  // action是import进来的actionCreators的合集对象
    decrease: action.decrease
  }, dispatch);
}

// bindActionCreators()就相当于
// {
//    increase: (...args) => dispatch(actions.increase(...args)),
//    decrease: (...args) => dispatch(actions.decrease(...args))
// }

const VisibleTodoList = connect(
  // 建立store的state与UI组件的props间的映射关系
  // 如果省略mapStateToProps,那么UI组件就不会订阅store,即store的更新不会引起UI组件的更新
  mapStateToProps, 
  // 用于建立store.dispatch方法和UI组件的props间的映射关系
  mapDispatchToProps
)(TodoList);
作者

BiteByte

发布于

2020-08-08

更新于

2024-11-15

许可协议