Redux学习笔记

253 阅读6分钟

1.Redux 核心

1.1 Redux介绍

JavaScript状态容器,提供可测试化的状态管理 容器:JavaScript对象 状态:每一个DOM元素都有状态,比如:一个按钮,按钮的“显示”或者“隐藏”就是状态 状态最终都会被抽象为数据保存在对象中,这个对象就是所谓的状态容器,容器中的数据与DOM元素的状态一一对应。

const state = {
    modelOpen: "yes",
    btnClicked: "no",
    btnActiveClass: 'active',
    page: 5,
    size: 10
}

redux就是一个JavaScript的状态容器,redux还提供一种科学化的状态管理方式,让状态的管理变得更加容易和可维护。

1.2 Redux核心概念及工作流程

image.png

在redux的应用当中,所有的状态都存储在store当中,视图不能直接操作store中的状态,除非触发action,action会被reducer接收,reducer内部会根据action的type属性值的不同对状态进行不同的处理,当reducer对状态处理之后再通过返回值的方式把状态返回给store以更新store中的状态,当store中的状态更新后,我们通过subscribe方法就知道状态更新了,然后再去同步视图中的状态。

image.png

  • createStore函数用来创建store状态容器,返回值就是存储状态的容器store对象,第一个参数是reducer函数,
  • reducer函数的作用是向store中存储状态,reducer返回什么store中就存储什么,reducer有两个参数,第一个参数是state及存储在store中的状态,第二个是action及接收到的action对象,内部根据action的type属性值的不同对store进行不同的状态处理
  • getState()用来获取store中存储的状态
  • subscribe()是用来订阅store的,当store中的状态发生变化,就会执行传入subscribe的回调函数,通常用这个方法得到store最新的状态以同步视图
  • dispatch()用来触发action,第一个参数接收一个action对象。在视图中想要触发action时就必须调用dispatch来触发action

2.React + Redux

2.1 在React中不使用Redux时遇到的问题

在React中组件通信的数据流是单向的,顶层组件可以通过props属性向下层组件传递数据,而下层组件不能向上层组件传递数据,要实现下层组件修改数据,需要上层组件传递修改数据的方法到下层组件,当项目越来越大的时候,组件之间传递数据变得越来越困难。

2.2 在React项目中加入Redux的好处

使用Redux管理数据,由于store独立于组件,使得数据管理独立于组件,解决了组件与组件之间传递数据困难的问题。各个组件可以直接从store中取获取和修改数据。
借助react-redux使react和redux更好的结合

  1. 通过provider组件 将 store 放在了全局的组件可以够的到的地方
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

import { Provider } from 'react-redux';
import { store } from './store';

ReactDOM.render(
  // 通过provider组件 将 store 放在了全局的组件可以够的到的地方
  <Provider store={store}><App/></Provider>,
  document.getElementById('root')
);
  1. connect 方法帮助简化从store中获取状态和dispatch方法,订阅store中的状态变更并且重新更新组件
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as couterActions from '../store/actions/counter.actions';

function Counter ({count, increment, decrement, increment_async}) {
  return <div>
    <button onClick={() => increment_async(20)}>+</button>
    <span>{count}</span>
    <button onClick={() => decrement(5)}>-</button>
  </div>
}

// 1. connect 方法会帮助我们订阅store 当store中的状态发生更改的时候 会帮助我们重新渲染组件
// 2. connect 方法可以让我们获取store中的状态 将状态通过组件的props属性映射给组件
// 3. connect 方法可以让我们获取 dispatch 方法

// mapStateToProps作为connect的第一个参数把state映射给props
const mapStateToProps = state => ({
  count: state.counter.count
});

// mapDispatchToProps作为connect的第二个参数把dispatch映射给props
const mapDispatchToProps = dispatch => bindActionCreators(couterActions, dispatch)

export default connect(mapStateToProps, mapDispatchToProps)(Counter);
// src/store/actions/counter.actions.js
export const increment = payload => ({type: 'increment', payload})
export const decrement = payload => ({type: 'decrement', payload})

3.Redux 中间件

3.1 什么是中间件

中间件就是一个函数,允许我们扩展redux应用程序。扩展和增强体现在对action的处理能力上,以前reducer接收到action后是直接处理的,加入中间件以后,action则优先被中间件处理,中间处理完action之后再传给reducer继续处理。

3.2 加入了中间件Redux工作流程

image.png

4.开发Redux中间件

4.1 开发中间件的模板代码

image.png 例如:

// middleware/logger.js
export default store => next => action => {
    console.log(store)
    console.log(action)
    next(action) // next必须不能忘记调用,否则其他中间件不会执行,reducer也不会执行
}

4.2 注册中间件

中间件在开发完以后只有被注册才能在Redux的工作流程中生效

import { createStore, applyMiddleware } from 'redux'
import logger from './middlewares/logger'

createStore(reducer, applyMiddleware(
    logger
))

5. Redux常用中间件

5.1 redux-thunk

作用:允许在redux的工作流中加入异步代码
5.1.1 下载
npm install redux-thunk
5.1.2 引入redux-thunk
import thunk from 'redux-thunk'
5.1.3 注册 redux-thunk

import { applyMiddleware } from 'redux'
createStore(rootReducer, applyMiddleware(thunk))

5.1.4 使用 redux-thunk 中间件

// redux-thunk的引入,actionCreators就不再仅仅只能返回action对象,而可以返回一个函数了,在函数里面执行异步操作,然后再dispatch另一个action把异步操作的返回结果传递给reducer
const loadPosts = () => async dispatch => {
    const posts = await axios.get('/api/posts').then(response => response.data)
    dispatch({ type: LOADPOSTSUCCESS, payload: posts })
}

5.2 redux-saga

作用:与 redux-thunk 一样都是可以在redux工作流程中加入异步代码,但更强大,它允许我们将异步操作从Action Creator文件中抽离出来,放在一个单独的文件中。使得项目代码更加可维护。
5.2.1 下载
npm install redux-saga
5.2.2 引入&&创建 redux-saga 中间件

import createSagaMiddleware from 'redux-saga'
const sagaMiddleware = creaeSagaMiddleware()

5.2.3 注册 sagaMiddleware
createStore(reducer, applyMiddleware(sagaMiddleware))
5.2.4 使用 saga 接收 action 执行异步操作

// src/store/sagas/post.saga.js
import { takeEvery, put } from 'redux-saga/effects'

function* load_posts () {
    const { data } = yeild axios.get('/api/posts.json')
    // put和dispatch的作用一样用来触发另一个acion,当异步操作执行完毕需要触发另一个action来把异步操作的返回结果传递给reducer
    yeild put(load_posts_success(data))
}

export default function* postSaga () {
    // takeEvery用来接收action,第一个参数是action类型字符串,第二个参数是当接收到这个类型后需要执行的方法,可以是个函数名字(函数定义在上面),或者直接写函数
    yeild takeEvery(LOAD_POSTS, load_posts)
}

5.2.5 启动saga
必须启动saga,saga才能加入到redux的工作流程中

import postSaga from './store/sagas/post.saga'
sagaMiddleware.run(postSaga)

5.3 redux-actions

作用:redux流程中大量的样板代码读写很痛苦,使用redux-actions可以简化Action和Reducer的处理
5.3.1 下载
npm install redux-acitons
5.3.2 创建Action

import { createAction } from 'redux-actions'

const increment = createAction('increment')
const decrement = createAction('decrement')

等价于:

const increment = payload => ({type: 'increment', payload})
const decrement = payload => ({type: 'decrement', payload})

就连参数都由react-action帮我们传递了,reducer中可以直接通过action.payload获取参数

5.3.3 创建 Reducer

import { handleActions as createReducer } from 'redux-actionss'
import { increment, decrement } from '../actions/counter.action'

const initialState = {count: 0}
const counterReducer = createReducer({
    [increment]: (state, action) => ({count: state.count + 1}),
    [decrement]: (state, action) => ({count: state.count - 1})
}, initialState)
export default counterReducer

相比原本的switch...case写法,reducer的写法变得简单的多!