简介

  • 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 的 视频
  • 相关官方文档

Redux 一步到位的更多相关文章

  1. 重写官方TodoList,对于初学react+redux的人来说,很有好处

    虽然官网的TodoList的例子写的很详细,但是都是一步到位,就是给你一个action,好家伙,全部都写好了,给你一个reducer,所有功能也是都写好了,但是我们这些小白怎么可能一下就消化那么多,那 ...

  2. 理顺react,flux,redux这些概念的关系

    作者:北溟小鱼hk链接:https://www.zhihu.com/question/47686258/answer/107209140来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转 ...

  3. RxJS + Redux + React = Amazing!(译一)

    今天,我将Youtube上的<RxJS + Redux + React = Amazing!>翻译(+机译)了下来,以供国内的同学学习,英文听力好的同学可以直接看原版视频: https:/ ...

  4. 通过一个demo了解Redux

    TodoList小demo 效果展示 项目地址 (单向)数据流 数据流是我们的行为与响应的抽象:使用数据流能帮我们明确了行为对应的响应,这和react的状态可预测的思想是不谋而合的. 常见的数据流框架 ...

  5. RxJS + Redux + React = Amazing!(译二)

    今天,我将Youtube上的<RxJS + Redux + React = Amazing!>的后半部分翻译(+机译)了下来,以供国内的同学学习,英文听力好的同学可以直接看原版视频: ht ...

  6. redux学习

    redux学习: 1.应用只有一个store,用于保存整个应用的所有的状态数据信息,即state,一个state对应一个页面的所需信息 注意:他只负责保存state,接收action, 从store. ...

  7. webpack+react+redux+es6开发模式

    一.预备知识 node, npm, react, redux, es6, webpack 二.学习资源 ECMAScript 6入门 React和Redux的连接react-redux Redux 入 ...

  8. Redux初见

    说到redux可能我们都先知道了react,但我发现,关于react相关的学习资料很多,也有各种各样的种类,但是关于redux简单易懂的资料却比较少. 这里记录一下自己的学习理解,希望可以简洁易懂,入 ...

  9. react+redux教程(八)连接数据库的redux程序

    前面所有的教程都是解读官方的示例代码,是时候我们自己写个连接数据库的redux程序了! 例子 这个例子代码,是我自己写的程序,一个非常简单的todo,但是包含了redux插件的用法,中间件的用法,连接 ...

随机推荐

  1. js基础——函数

    1.函数声明:通过函数可封装任意多条语句,且可在任意地方.任何时候调用执行. eg. function box(){//无参函数      alert("只有函数被调用,我才会被执行&quo ...

  2. vue基于 element-ui 实现菜单动画效果,任意添加 li 个数均匀撑满 ul 宽度

    %)%)%%%))) .) .) .) .) .) .) .) .) .) .) .) .) .) .) .) .% %% %deg);}

  3. linux 自动检测 IRQ 号

    驱动在初始化时最有挑战性的问题中的一个是如何决定设备要使用哪个 IRQ 线. 驱动需 要信息来正确安装处理. 尽管程序员可用请求用户在加载时指定中断号, 这是个坏做法, 因为大部分时间用户不知道这个号 ...

  4. Python3装饰器的使用

    装饰器 简易装饰器模板 def wrapper(func): def inner(*args,**kwargs): print('主代码前添加的功能') ret=func(*args,**kwargs ...

  5. poj2826 An Easy Problem?!(计算几何)

    传送门 •题意 两根木块组成一个槽,给定两个木块的两个端点 雨水竖直下落,问槽里能装多少雨水, •思路 找不能收集到雨水的情况 我们令线段较高的点为s点,较低的点为e点 ①两条木块没有交点 ②平行或重 ...

  6. IntPtr、Struct 相互转换

    一般写c#代码基本用不到 相互转换 只有调用c++中的dll动态库的时候才用的到 struct转intptr public static IntPtr StructToIntPtr<T>( ...

  7. springmvc 参数校验/aop失效/@PathVariable 参数为空

    添加依赖 <!-- 参数校验 --> <dependency> <groupId>org.hibernate.validator</groupId> & ...

  8. docker(整理中

    docker镜像默认的下载地址就是docker的官网,而他们的官网在国内没有节点,时不时就被国家防火墙隔绝,会出现DNS解析不到,或者找不到镜像等狗血提示. 解决的方法有三个: 第一,就是不断尝试,因 ...

  9. Vue+Element实现网页版个人简历系统

    这篇文章介绍一个使用Vue+Element实现的个人简历系统,主要用到的技术有:vue.element.css3.css定位. 作者在window10进行开发,目前只在chrome上进行过测试,没有大 ...

  10. 从零开始のcocos2dx生活(八)ParticleSystemQuad

    https://learnopengl-cn.github.io/01%20Getting%20started/04%20Hello%20Triangle/#_1 写的真的非常好-最近没时间拜读,只看 ...