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 的 视频
- 相关官方文档
Redux 一步到位的更多相关文章
- 重写官方TodoList,对于初学react+redux的人来说,很有好处
虽然官网的TodoList的例子写的很详细,但是都是一步到位,就是给你一个action,好家伙,全部都写好了,给你一个reducer,所有功能也是都写好了,但是我们这些小白怎么可能一下就消化那么多,那 ...
- 理顺react,flux,redux这些概念的关系
作者:北溟小鱼hk链接:https://www.zhihu.com/question/47686258/answer/107209140来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转 ...
- RxJS + Redux + React = Amazing!(译一)
今天,我将Youtube上的<RxJS + Redux + React = Amazing!>翻译(+机译)了下来,以供国内的同学学习,英文听力好的同学可以直接看原版视频: https:/ ...
- 通过一个demo了解Redux
TodoList小demo 效果展示 项目地址 (单向)数据流 数据流是我们的行为与响应的抽象:使用数据流能帮我们明确了行为对应的响应,这和react的状态可预测的思想是不谋而合的. 常见的数据流框架 ...
- RxJS + Redux + React = Amazing!(译二)
今天,我将Youtube上的<RxJS + Redux + React = Amazing!>的后半部分翻译(+机译)了下来,以供国内的同学学习,英文听力好的同学可以直接看原版视频: ht ...
- redux学习
redux学习: 1.应用只有一个store,用于保存整个应用的所有的状态数据信息,即state,一个state对应一个页面的所需信息 注意:他只负责保存state,接收action, 从store. ...
- webpack+react+redux+es6开发模式
一.预备知识 node, npm, react, redux, es6, webpack 二.学习资源 ECMAScript 6入门 React和Redux的连接react-redux Redux 入 ...
- Redux初见
说到redux可能我们都先知道了react,但我发现,关于react相关的学习资料很多,也有各种各样的种类,但是关于redux简单易懂的资料却比较少. 这里记录一下自己的学习理解,希望可以简洁易懂,入 ...
- react+redux教程(八)连接数据库的redux程序
前面所有的教程都是解读官方的示例代码,是时候我们自己写个连接数据库的redux程序了! 例子 这个例子代码,是我自己写的程序,一个非常简单的todo,但是包含了redux插件的用法,中间件的用法,连接 ...
随机推荐
- python编程设计模式之接口类和抽象类
接口类 """ 接口类 是一种编程设计模式,在python原本没有接口类 借用Java思想创建的一种规范设计模式 支持多继承,进行多方面规范 ""&q ...
- canvas.toDataURL 由于跨域报错的解决方法
关于canvas.toDataURL 由于跨域报错的解决方法 用过canvas,都知道toDataURL这个方法真好用,不仅合成图片用到它,压缩图片也用到它.但有一个问题,就是图片源不能跨域,不然会报 ...
- React 编写项目连环套路
搭建一个项目 公共部分放一块:index.js style.js store.js reducer.js actionCreateors.js actionTypes.js 分页 一块一 ...
- 从http到https--phpStudy2018
0. 将SSL证书解压到以下目录,申请方式见 百度 Apache/cert/ 分别更名为 my_public.crt my.key my_chain.crt 1. phpStudy->其它选项菜 ...
- P1045 和为给定数
题目描述 给出若干个整数,询问其中是否有一对数的和等于给定的数. 输入格式 共三行: 第一行是整数 \(n(0 \lt n \le 100000)\) ,表示有 \(n\) 个整数. 第二行是n个整数 ...
- Git用在公司项目上的操作
修改代码后再次提交 搭档优化好他自己的代码后,我想在vscode上看看他优化后的结果 此时直接git pull origin就可以看到了 j接下来的一些指令,慢慢了解... 分支本身已经在我上面 以下 ...
- MySQL 命令行(转)
1.登录mysql 本地:mysql -u root -p, 回车后输入密码; 也可以p后不加空格,直接加密码.回车就登录了 远程:mysql -hxx.xx.xx.xx -u -pxxx 2.查看数 ...
- HDU1251 统计难题[map的应用][Trie树]
一.题意 给出一组单词,另给出一组单词用作查询,求解对于每个用于查询的单词,前一组中有多少个单词以其为前缀. 二.分析 根据题目很容易想到hash的方法,首先可以朴素的考虑将第一组中的所有单词的前缀利 ...
- Spring MVC 模拟
在Spring MVC中,将一个普通的java类标注上Controller注解之后,再将类中的方法使用RequestMapping注解标注,那么这个普通的java类就够处理Web请求,示例代码如下: ...
- 22.json&pickle&shelve
转载:https://www.cnblogs.com/yuanchenqi/article/5732581.html json 之前我们学习过用eval内置方法可以将一个字符串转成python对象,不 ...