Redux与React Redux简单上手

介绍

工作流

在这里插入图片描述

  • React Components:组件,相当于借书人。
  • Action:相当于 ‘我要借**书’,这句话。
  • Store:相当于 图书馆管理员。
  • Reducer:相当于 本子,记录所有图书的位置,让图书馆管理员知道那本书在哪里,找出来。

三大原则

  • 单一数据源:整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。
  • State是只读的:唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。
  • 使用纯函数来执行修改:为了描述 action 如何改变 state tree ,你需要编写 reducers。

Redux设计理念

如果actionreducerstore的关系已经应用不是很熟,可以先跳过这部分。
Redux 的 React 绑定库是基于 容器组件和展示组件相分离 的开发思想

可以直接使用 store.subscribe() 来编写容器组件。但不建议这么做的原因是无法使用 React Redux 带来的性能优化。也因此,不要手写容器组件,而使用 React Redux 的 connect() 方法来生成

connect(
     mapStateToProps,
     mapDispatchToProps
)(关联到的组件)

使用 connect() 前,需要先定义

  • mapStateToProps 这个函数来指定如何把当前 Redux store state 映射到展示组件的 props
  • mapDispatchToProps() 方法接收 dispatch() 方法并返回期望注入到展示组件的 props 中的回调方法。

我的理解是 用来处理Redux store state和props中的回调。

const mapStateToProps = state => {
  return {
    todos: getVisibleTodos(state.todos, state.visibilityFilter)
  }
}

const mapDispatchToProps = dispatch => {
  return {
    onTodoClick: id => {
      dispatch(toggleTodo(id))
    }
  }
}

基础

Action

Action 是把数据从应用传到 store 的有效载荷。它是 store 数据的唯一来源。一般来说你会通过 store.dispatch()action 传到 store
Action 本质上是 JavaScript 普通对象。我们约定,action 内必须使用一个字符串类型的 type 字段来表示将要执行的动作。多数情况下,type 会被定义成字符串常量。当应用规模越来越大时,建议使用单独的模块或文件来存放 action。

import { ADD_TODO, REMOVE_TODO } from '../actionTypes'
{
  type: ADD_TODO,
  text: 'Build my first Redux app'
}

需要再添加一个 action index 来表示用户完成任务的动作序列号。因为数据是存放在数组中的,所以我们通过下标 index 来引用特定的任务。而实际项目中一般会在新建数据的时候生成唯一的 ID 作为数据的引用标识。
应该尽量减少在 action 中传递的数据。因此传index会比传整个对象好

Action创建函数

Action 创建函数 就是生成 action 的方法。“action” 和 “action 创建函数” 这两个概念很容易混在一起,使用时最好注意区分。
在 Redux 中的 action 创建函数只是简单的返回一个 action:

// Action types
import {CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM} from './actionTypes'

// Action creator
export const getInputChangeAction = (value) => ({
    type: CHANGE_INPUT_VALUE,
    value
});

Reducer

Reducers 指定了应用状态的变化如何响应 actions 并发送到 store,记住 actions 只是描述了有事情发生了这一事实,并没有描述应用如何更新 state

永远不要在 reducer 里做这些操作

  • 修改传入参数
  • 执行有副作用的操作,如 API 请求和路由跳转;
  • 调用非纯函数,如 Date.now() 或 Math.random()。

只要传入参数相同,返回计算得到的下一个 state 就一定相同。没有特殊情况、没有副作用,没有 API 请求、没有变量修改,单纯执行计算。

import {CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM} from './actionTypes';

const defaultState = {
    inputValue: '',
    list: []
};

// state 是整个store的数据
// reducer 可以接受state,但绝对不能修改state
export default (state = defaultState, action) => {
    switch (action.type) {
        case CHANGE_INPUT_VALUE:
            const newState = JSON.parse(JSON.stringify(state));
            newState.inputValue = action.value;
            return newState;
        case ADD_TODO_ITEM:
            const addState = JSON.parse(JSON.stringify(state));
            addState.list.push(addState.inputValue);
            addState.inputValue = '';
            return addState;
        case DELETE_TODO_ITEM:
            const delState = JSON.parse(JSON.stringify(state));
            delState.list.splice(action.index, 1);
            return delState;
        default:
            return state;
    }
}

注意:

  1. 不要修改state。(因此一般会克隆或者返回新对象)
  2. 在遇到未知action的时候一定要返回就的state。

当遇到大项目的时候,会把一个Reducer 分成很多个小的Reducer
注意每个 reducer 只负责管理全局 state 中它负责的一部分。每个 reducerstate 参数都不同,分别对应它管理的那部分 state 数据。

Redux 提供了 combineReducers() 工具类将多个reducer合并成一个reducer

combineReducers() 所做的只是生成一个函数,这个函数来调用你的一系列 reducer,每个 reducer 根据它们的 key 来筛选出 state 中的一部分数据并处理,然后这个生成的函数再将所有 reducer 的结果合并成一个大的对象。
例:

import { combineReducers } from 'redux'

const todoApp = combineReducers({
  visibilityFilter,
  todos
})

export default todoApp

Store

使用 action 来描述“发生了什么”,和使用 reducers 来根据 action 更新 state 的用法。Store 就是把它们联系到一起的对象。Store 有以下职责:

  • 维持应用的 state
  • 提供 getState() 方法获取 state
  • 提供 dispatch(action) 方法更新 state
  • 通过 subscribe(listener) 注册监听器;
  • 通过 subscribe(listener) 返回的函数注销监听器

Redux 应用只有一个单一的 store。当需要拆分数据处理逻辑时,你应该使用 reducer 组合而不是创建多个 store

使用createStore() 来创建 store

  • 第一个参数是reducers;
  • 第二个参数是可选的, 用于设置 state 初始状态。这对开发同构应用时非常有用,服务器端 redux 应用的 state 结构可以与客户端保持一致, 那么客户端可以将从网络接收到的服务端 state 直接用于本地数据初始化。
import {createStore} from "redux";
import reducer from "./reducer";

// 创建store 必须要有reducer
const store = createStore(
    reducer,
    window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());

export default store;

使用 chrome 插件

在 chrome 浏览器中安装 redux-devtools 插件。
然后回到你的项目里,在你创建store的时候传入第二个参数:

window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());

数据流

Redux 应用中数据的生命周期遵循下面 4 个步骤

  1. 调用 store.dispatch(action)
  2. Redux store 调用传入reducer 函数。
  3. reducer 应该把多个子 reducer 输出合并成一个单一的 state。Redux 原生提供combineReducers()辅助函数,来把根 reducer 拆分成多个函数,用于分别处理 state 树的一个分支。
  4. Redux store 保存了根 reducer 返回的完整 state。所有订阅 store.subscribe(listener) 的监听器都将被调用;监听器里可以调用 store.getState() 获得当前 state

Redux中间件

在这里插入图片描述
简单来说Redux中间件,指的是actionstore的中间dispatch,也可以说是dispatch的升级封装。

  • redux中间件redux-thunk:把异步代码放到action中处理,即action creator返回是可以是一个函数。

  • redux中间件redux-saga:把异步代码或者复杂逻辑拆分放到一个文件里,当dispatch一个action的时候,不仅reducer可以接收到actionsaga文件也能接收到actionsaga中使用put来进行dispatch一个action

个人理解:在页面上dispatch一个action,然后另一个然后处理异步操作后,再重新dispatch一个新的action

React Redux

  • 安装npm i -S react-redux
  • 使用
  1. Provider 包裹要渲染的组件,利用其 store属性 连接 store
import store from "./store/index";

const App = (
    // Provider 提供器,用于连接store,其内部组件就可以获取store数据(利用connect)
    <Provider store={store}>
        <TodoList/>
    </Provider>
);

ReactDOM.render(App, document.getElementById('root'));
  1. 组件利用 connectstore 连接,其中 connect 接收两个参数:mapStateToProps 把 store 里的数据(即全局 state 的数据),映射为组件 props 的数据。 mapDispatchToPropsstore.dispatch 挂载到 props
// 把 store 里的state数据,映射为组件 props 的数据
const mapStateToProps = (state) => {
    return {
        inputValue: state.inputValue,
        list: state.list
    }
};

// store.dispatch 挂载到 props
const mapDispatchToProps = (dispatch) => {
    return {
        changeInputValue(e) {
            const action = getInputChangeAction(e.target.value);
            dispatch(action);
        },
        handleClick(e) {
            const action = getAddItemAction();
            dispatch(action);
        },
        handleDelete(index) {
            const action = getDeleteItemAction(index);
            dispatch(action);
        }
    }
};

// connect 用组件与 store 做连接, 实际 connect 返回导出的组件就为容器组件
export default connect(mapStateToProps, mapDispatchToProps)(TodoListUI);

两个小demo

两个都是实现简单的 todoList,只是实现的形式有少许不一样

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值