Redux 一步到位

简介

  • Redux 是 JavaScript 状态容器,提供可预测化的状态管理
  • Redux 除了和 React 一起用外,还支持其它库( jquery ... )
  • 它体小精悍(只有2kB,包括依赖)
  • 由 Flux 演变而来,但受 Elm 的启发,避开了 Flux 的复杂性。

安装

  • 稳定版 npm install --save redux
  • 附加包 React 绑定库 npm install --save react-redux
  • 附加包 开发者工具 npm install --save-dev redux-devtools

创建 reducer.js

应用中所有的 state 都以一个对象树的形式储存在一个单一的 store 中。 惟一改变 state 的办法是触发 action,一个描述发生什么的对象。 为了描述 action 如何改变 state 树,先编写 reducers。

const defaultState = {}
export default (state = defaultState,action)=>{
    return state
}

创建 Store

import { createStore } from 'redux'
import reducer from './reducer'
const store = createStore(reducer)
export default store
  • 使用 createStore 创建数据储存仓库
  • 将 store 暴露出去

获取 store

组件来获取 store 中的数据

import store from './store'
// ...
constructor(props){
    super(props)
    console.log(store.getState())
}
  • 先引入store
  • 使用 getState 函数获取数据

安装 Redux DevTools

chrome 搜索插件 Redux DevTools 并安装

 import { createStore } from 'redux'
 import reducer from './reducer'
 const store = createStore(reducer,
+ window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__())
 export default store
  • 只是添加了一句话
  • 意思是看window里有没有这个方法,有则执行这个方法
  • 启动项目就可以看到 State 了

Action

Action 是 store 数据的唯一来源。

创建 action

const action ={
        type:'',
        value: ''
    }

store.dispatch(action)
  • type 字段来表示将要执行的动作(必须要有)
  • 除了 type 字段外,action 对象的结构完全自由
  • 使用 dispatch 函数发送数据到 store

更改 Reducer

export default (state = defaultState,action)=>{
    if(action.type === 'changeInput'){
        let newState = JSON.parse(JSON.stringify(state)) //深度拷贝state
        newState.inputValue = action.value
        return newState
    }
    return state
}
  • 先判断type是否正确,如果正确,声明一个变量newState
  • Reducer 里只能接收 state,不能改变 state,所以将新变量 return

更新组件数据

constructor(props){
    // ...
    storeChange(){
     this.setState(store.getState())
    }
    this.storeChange = this.storeChange.bind(this)
    store.subscribe(this.storeChange)
}
  • bing(this) 转变this指向
  • storeChange 重新setState
  • subscribe 函数用来订阅 store 状态

小技巧

抽离 Action Types

使用单独的模块或文件来定义 action type 常量并不是必须的,甚至根本不需要定义。对于小应用来说,使用字符串做 action type 更方便些。不过,在大型应用中把它们显式地定义成常量还是利大于弊的。

actionTypes.js

const ADD_TODO = 'ADD_TODO';
const REMOVE_TODO = 'REMOVE_TODO';
const LOAD_ARTICLE = 'LOAD_ARTICLE';

组件中引用

import { ADD_TODO , REMOVE_TODO , LOAD_ARTICLE } from './store/actionTypes'

相应的 Reducer 也要更改

import { ADD_TODO , REMOVE_TODO , LOAD_ARTICLE } from './store/actionTypes'

const defaultState = {}
export default (state = defaultState,action)=>{
    if(action.type === ADD_TODO){
        let newState = JSON.parse(JSON.stringify(state))
        newState.inputValue = action.value
        return newState
    }
    // ...
    return state
}

抽离 Redux Action

Action 创建函数 就是生成 action 的方法。注意与 action 概念相区分。

function addTodo(text) {
  return {
    type: ADD_TODO,
    text
  }
}

actionCreators.js

function addTodo(text) {
  return {
    type: ADD_TODO,
    text
  }
}

组件中使用

import { addTodo } from './actionCreators';

// ...
dispatch(addTodo('Use Redux'))

注意

  • store 必须是唯一的
  • 只有store能改变自己的内容,Reducer不能改变
  • Reducer必须是纯函数

拆分组件UI和业务逻辑

TodoListUI.js

import React, { Component } from 'react';
class TodoListUi extends Component {

    render() {
        return ( <div>123</div> );
    }
}

export default TodoListUi;

TodoList.js

import TodoListUI from './TodoListUI'

render() {
    return (
        <TodoListUI />
    );
}
  • constructor 中对于对应方法要重新绑定 this
  • 修改完 TodoList.js 文件,还要对UI组件进行对应的属性替换

无状态组件

  • 无状态组件其实就是一个函数
  • 不用继承任何的 class
  • 不存在 state
  • 因为无状态组件其实就是一个函数, 性能比普通的React组件好

TodoListUi 改写成无状态组件

import React from 'react';

const TodoListUi = (props)=>{
    return(
        <> some code </>
    )
}

export default TodoListUi;

Axios 异步获取数据和 Redux 结合

不过就是走一遍上面的流程

actionCreatores.js

export const getListAction  = (data)=>({
    type: xxx,
    data
})

组件

import axios from 'axios'
import {getListAction} from './store/actionCreatores'

componentDidMount(){
    axios.get('https:// xxx').then((res)=>{
        const data = res.data
        const action = getListAction(data)
        store.dispatch(action)
    })
}

reducer.js

import {GET_LIST} from './actionTypes'

const defaultState = {
    list:[]
}
export default (state = defaultState,action)=>{
    if(action.type === GET_LIST ){
        let newState = JSON.parse(JSON.stringify(state))
        newState.list = action.data.data.list
        return newState
    }

    return state
}

Redux 中间件

注意不是 react 中间件

Redux-thunk

  • Redux-thunk
  • Redux-thunk 是对 Redux 中 dispatch 的加强
npm install --save redux-thunk
import { createStore , applyMiddleware } from 'redux'
import thunk from 'redux-thunk'

const store = createStore(
    reducer,
    applyMiddleware(thunk)
)
  • 使用中间件需要先引入 applyMiddleware
  • 可以这样 但是我们使用 Dev Tool 占用了第二个参数

所以我们这样写

import { createStore , applyMiddleware ,compose } from 'redux'

const composeEnhancers =   window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}):compose
const enhancer = composeEnhancers(applyMiddleware(thunk))
const store = createStore( reducer, enhancer)
export default store
  • 利用compose创造一个增强函数 composeEnhancers,就相当于建立了一个链式函数
  • 把thunk加入 ( applyMiddleware(thunk) )
  • 直接在createStore函数中的第二个参数,使用这个 enhancer 变量

在 actionCreators.js 中写业务

actionCreators.js 都是定义好的 action,根本没办法写业务逻辑,有了Redux-thunk之后,可以把TodoList.js中的 componentDidMount 业务逻辑放到这里来编写。

import axios from 'axios'

//...
export const getTodoList = () =>{
    return (dispatch)=>{
        axios.get('https:// xxx ').then((res)=>{
            const data = res.data
            const action = getListAction(data)
            dispatch(action)
        })
    }
}

以前的action是对象,现在的action可以是函数了,这就是redux-thunk带来的好处

组件中

import { getTodoList } from './store/actionCreatores'
// ...
componentDidMount(){
    const action = getTodoList()
    store.dispatch(action)
}

Redu-saga

安装

npm install --save redux-saga

store/index.js

import createSagaMiddleware from 'redux-saga'
const sagaMiddleware = createSagaMiddleware();
  • 引入saga
  • 创建saga中间件

Redux-thunk 替换成 saga

import { createStore , applyMiddleware ,compose } from 'redux'
import reducer from './reducer'
import createSagaMiddleware from 'redux-saga'

const sagaMiddleware = createSagaMiddleware();
const composeEnhancers =   window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}):compose
const enhancer = composeEnhancers(applyMiddleware(sagaMiddleware))

const store = createStore( reducer, enhancer)
export default store

创建 store/sagas.js

import {takeEvery, put} from 'redux-saga/effects'
import {GET_MY_LIST} from './actionTypes'
import {getListAction} from './actionCreatores'
import axios from 'axios'

//generator函数
function* mySaga() {
    //等待捕获action
    yield takeEvery(GET_MY_LIST, getList)
}

function* getList(){
    const res = yield axios.get('https://www.easy-mock.com/mock/5cfcce489dc7c36bd6da2c99/xiaojiejie/getList')
    const action = getListAction(res.data)
    yield put(action)
}

export default mySaga;

store/index.js

import { createStore , applyMiddleware ,compose } from 'redux'  //  引入createStore方法
import reducer from './reducer'
import createSagaMiddleware from 'redux-saga'
import mySagas from './sagas'

const sagaMiddleware = createSagaMiddleware();
const composeEnhancers =   window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}):compose
const enhancer = composeEnhancers(applyMiddleware(sagaMiddleware))
const store = createStore( reducer, enhancer)

sagaMiddleware.run(mySagas)

export default store

react-redux

react-redux 不是 redux,
React-Redux 是 Redux 的官方 React 绑定库。它能够使你的 React 组件从 Redux store 中读取数据,并且向 store 分发 actions 以更新数据

npm install --save react-redux

是一个提供器,只要使用了这个组件,组件里边的其它所有组件都可以使用store了

import React from 'react';
import ReactDOM from 'react-dom';
import TodoList from './TodoList'
import { Provider } from 'react-redux'
import store from './store'
const App = (
    <Provider store={store}>
        <TodoList />
    </Provider>
)
ReactDOM.render(App, document.getElementById('root'));

connect 连接器

  • connect 可用来获取 store 中的数据
  • connect 的作用是把UI组件(无状态组件)和业务逻辑代码的分开,然后通过connect再链接到一起,让代码更加清晰和易于维护。

先制作映射关系,映射关系就是把原来的state映射成组件中的props属性

const stateToProps = (state)=>{
    return {
            inputValue: state.inputValue
    }
}

使用 connect 获取 store 中的数据

import {connect} from 'react-redux'
export default connect(inputValue, null)(TodoList); // 这里的 inputValue 代表一个映射关系

修改 store 中的数据

例子:当我们修改中的值时,去改变store数据,UI界面也随之进行改变。

import React, { Component } from 'react';
import store from './store'
import { connect } from 'react-redux'

class TodoList extends Component {
    constructor(props){
        super(props)
        this.state = store.getState()
    }
    render() {
        return (
            <div>
                <div>
                    <input value={this.props.inputValue} onChange={this.props.inputChange} />
                    <button>提交</button>
                </div>
                <ul>
                    <li></li>
                </ul>
            </div>
            );
    }
}
const stateToProps = (state)=>{
    return {
        inputValue : state.inputValue
    }
}

const dispatchToProps = (dispatch) =>{
    return {
        inputChange(e){
            console.log(e.target.value)
        }
    }
}

export default connect(stateToProps,dispatchToProps)(TodoList);

派发 action 到 store 中 (再走一遍流程)

const dispatchToProps = (dispatch) =>{
    return {
        inputChange(e){
            let action = {
                type:'change_input',
                value:e.target.value
            }
            dispatch(action)
        }
    }
}

reducer

const defalutState = {
    inputValue : 'jspang',
    list :[]
}
export default (state = defalutState,action) =>{
    if(action.type === 'change_input'){
        let newState = JSON.parse(JSON.stringify(state))
        newState.inputValue = action.value
        return newState
    }
    return state
}

参考资料

  • 哔哩哔哩 jspang 的 视频
  • 相关官方文档

原文地址:https://www.cnblogs.com/guangzan/p/12215069.html

时间: 2024-10-03 14:45:09

Redux 一步到位的相关文章

写了两篇文章,对于初学react+redux的人来说,很有好处

虽然官网的TodoList的例子写的很详细,但是都是一步到位,就是给你一个action,好家伙,全部都写好了,给你一个reducer,所有功能也是都写好了,但是我们这些小白怎么可能一下就消化那么多,那我们就来拆解,一步一步实现,试想我们开发程序也是一个一个功能区域实现,那么我们第一步就是先把整体结构构思出来,然后先把头部做出来,这样看是不是简单很多! 将持续更新react+redux 链接1:http://www.cnblogs.com/heigehe/articles/6237362.html

Redux 的基础概念-API

三个基本原则 整个应用只有唯一一个可信数据源,也就是只有一个 Store State 只能通过触发 Action 来更改 State 的更改必须写成纯函数,也就是每次更改总是返回一个新的 State,在 Redux 里这种函数称为 Reducer Actions Action 很简单,就是一个单纯的包含 { type, payload } 的对象,type 是一个常量用来标示动作类型,payload 是这个动作携带的数据.Action 需要通过 store.dispatch() 方法来发送. 比

ReactJS React+Redux+Router+antDesign通用高效率开发模板,夜间模式为例

工作比较忙,一直没有时间总结下最近学习的一些东西,为了方便前端开发,我使用React+Redux+Router+antDesign总结了一个通用的模板,这个技术栈在前端开发者中是非常常见的. 总的来说,我这个工程十分便捷,对于初学者来说,可能包含到以下的一些知识点: 一.React-Router的使用 Router是为了方便管理组件的路径,它使用比较简单,一般定义如下就行,需要注意的是,react-router的版本有1.0-3.0,各个版本对应的API大致相似,但也有不同,我使用的是2.X的,

redux+flux(一:入门篇)

React是facebook推出的js框架,React 本身只涉及UI层,如果搭建大型应用,必须搭配一个前端框架.也就是说,你至少要学两样东西,才能基本满足需要:React + 前端框架. Facebook官方使用的是 Flux 框架.本文就介绍如何在 React 的基础上,使用 Flux 组织代码和安排内部逻辑. 首先,Flux将一个应用分成四个部分: Flux 的最大特点,就是数据的"单向流动". 用户访问 View View 发出用户的 Action Dispatcher 收到

redux 初步理解

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica; color: #454545 } p.p2 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica; color: #454545; min-height: 14.0px } span.s1 { font: 12.0px "PingFang SC" } 派发一个 action 给 reducer, r

理解Javascript的状态容器Redux

Redux要解决什么问题? 随着 JavaScript 单页应用开发日趋复杂,JavaScript 需要管理比任何时候都要多的 state (状态). 这些 state 可能包括服务器响应.缓存数据.本地生成尚未持久化到服务器的数据,也包括 UI 状态,如激活的路由,被选中的标签,是否显示加载动效或者分页器等等.管理不断变化的 state 非常困难.如果一个 model 的变化会引起另一个 model 变化,那么当 view 变化时,就可能引起对应 model 以及另一个 model 的变化,依

redux数据流

redux使用 reducer 来进行事件的处理,reducer 是一个纯函数,这个函数被表述为 (previousState, action) => newState ,它根据应用的状态和当前的 action 推导出新的 state.Redux 中有多个 reducer,每个 reducer 负责维护应用整体 state 树中的某一部分,多个 reducer 可以通过 combineReducers 方法合成一个根reducer,这个根reducer负责维护完整的 state. 当一个 act

数据流程redux

思考题: react+redux开发这么一个原型,要怎么开发? 整个redux流程的逻辑非常清晰,数据流是单向循环的,就像一个生产的流水线: store(存放状态) -> Container(显示状态) -> reducer (处理动作)-> store redux画图理解: redux 只是定义了应用的数据流程,只解决了 "数据层"(model layer) 的问题, 一般还会使用 react, angular 等作为"显示层" (UI laye

Flux --&gt; Redux --&gt; Redux React 入门 基础实例使用

本文的目的很简单,介绍Redux相关概念用法 及其在React项目中的基本使用 假设你会一些ES6.会一些React.有看过Redux相关的文章,这篇入门小文应该能帮助你理一下相关的知识 一般来说,推荐使用 ES6+React+Webpack 的开发模式,但Webpack需要配置一些东西,你可以先略过,本文不需要Webpack基础 入门,只是一些基础概念和用法的整理,更完整的内容推荐去看看文档,英文,中文 (不过我个人认为,官方文档的例子相对来说太复杂了,很难让新手马上抓住重点) (官方的例子正