Redux
关于redux
redux特征
- Single Source of Truth
- (State + action) => newstate 可预测性
- 纯函数更新Store
- state一个数据对象
- action一个描述变更state的对象
- reducer函数,数学理解为约归,这个函数的作用就是(State + action) => newstate
state发生任何变化一定是由一个action引起的,如果发现state出了问题,一定可以发现对应的action是什么,很容易去定位问题
理解Store
通过发布订阅模式进行数据管理,通过getState()获取state。
- 通过dispatch(action) + oldState使用reducer函数返回newState
- listener(通常是render()函数)订阅state的变化,发现变化后执行listener(重新render页面)
- getState()
- dispatch(action)
- subscribe(listener)
combinReducers
如果有多个reducer函数,将它们返回的state合并后放在在一个store里,有自己的节点,使用方法见下例
bindActionCreators
Action Creator函数生成action,该方法将生成的action和store.dispacth
方法装饰为新的函数,使用见下例
一个简单例子
一个简单的例子,在原生JavaScript中使用redux,来更好的理解redux
//在createStore时会dispach一次初始化store //reducer1 function stateChange(state, action){ if( state === undefined){ return 0 } switch(action.type){ case 'ADD': return state + action.payload case 'MINUS': return state - 1 default: return state } } //reducer2 const todos = (state = {}) => state //Creat store const store = Redux.createStore( //combinReducers的用法,生成两个节点 Redux.combineReducers({ stateChange, todos }) ) function add1(){ store.dispatch({type: 'ADD', payload:1 }) } //ActionCreator 结合bindActionCreators的用法 //add2 和 add1 是一样的效果 function add2(){ return {type: 'ADD',payload: 2} } add2 = Redux.bindActionCreators(add2, store.dispatch) function minus1(){ store.dispatch({ type: 'MINUS'}) } function addIfOdd(){ if(store.getState() % 2 !== 0){ store.dispatch({type: 'ADD', payload: 1}) } } function delay2sAdd(){ setTimeout(()=>{ store.dispatch({type:'ADD', payload: 1}) },2000) } function render(store){ let app = document.querySelector('#app') app.innerHTML = `
Clicked:${store.getState().stateChange}
` } render(store)//render页面 store.subscribe(()=>{ render(store) //listener函数订阅state变化,重新渲染页面 })
react-redux
如果我们像在原生JavaScript里使用redux,就需要在根组件将state和触发action的函数通过props一层一层的传递下去,这样使用状态管理显然不方便,所以诞生了react-redux
常用API
- Provider()
- connect()
- mapStateToProps()
- mapDispatchToProps()
一般流程
- 创建reducer
- 合并reducers
- createStore
- Provider
- connect(mapStatetoProps, mapDispatchToProps)(App)
例子
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';import App from './App';
import {createStore} from 'redux'
import { Provider } from 'react-redux'
const initialState = { n: 0}
function reducer(state = initialState, action){
switch(action.type){
case 'ADD':
return {n: state.n + action.payload}
case 'MINUS':
return {n:state.n - 1}
default:
return state
}
}
const store = createStore(reducer)
ReactDOM.render(
,
document.getElementById('root'));
// const render = () =>{
// ReactDOM.render({store.dispatch({type: 'ADD', payload: 1 })}}
// />, document.getElementById('root'));
// }
// render()
// store.subscribe(()=>{
// render()
// })
App.js
import React from 'react';
import {bindActionCreators} from 'redux'
import { connect } from 'react-redux'
function App(props) {
return (
Clicked:{props.count}
);
}
function add1(){
return {type: 'ADD',payload: 1}
}
function add2(){
return {type: 'ADD', payload: 2}
}
function minus(){
return {type: 'MINUS'}
}
const mapStateToProps = (state) => {
return { count: state.n } //这里考虑性能问题,将能访问的节点限制到最小的范围
}
const mapDispatchToProps = (dispatch) => {
return bindActionCreators({add1, add2, minus},dispatch)
// add1: () =>{dispatch({type:'ADD',payload:1 })},
// add2: () => {dispatch({type:'ADD', payload: 2 })},
// minus: () =>{dispatch({type:'MINUS'})}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(App)
异步Action & middleware
我们在使用connect调用Action creator函数的时候,同步的action会被自动添加上dispatch,异步的action被当作返回一个undefined。
如何解决:
异步actionCreator => middleware处理生成新的action => 手动dispatch => reducer
redux-thunk
- 这时要让actionCreator返回一个方法,该方法的参数是dispatch,该方法里可以进行一些异步的操作或条件判断后,回调进行手动的dispatch一个同步的action。
- middleware将判断actionCreator返回的是一个函数还是一个action
例子
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';import App from './App';
import {createStore, applyMiddleware} from 'redux'
import { Provider } from 'react-redux'
import thunk from 'redux-thunk'
const initialState = { n: 0}
function reducer(state = initialState, action){
switch(action.type){
case 'ADD':
return {n: state.n + action.payload}
case 'MINUS':
return {n:state.n - 1}
default:
return state
}
}
export const store = createStore(reducer, applyMiddleware(thunk))
ReactDOM.render(
,
document.getElementById('root'));
App.js
import React from 'react';
import {bindActionCreators} from 'redux'
import { connect } from 'react-redux'
import { store } from './index'
function App(props) {
return (
Clicked:{props.count}
);
}
function add1(){
return {type: 'ADD',payload: 1}
}
function add2(){
return {type: 'ADD', payload: 2}
}
function minus(){
return {type: 'MINUS'}
}
//使用redux-thunk
function oddAdd(){
return (dispatch) => {
if(store.getState().n % 2 ===1 ){
dispatch(add1())
}
}
}
//异步action,使用redux-thunk
function delay2s(){
return (dispatch) => {
setTimeout(()=> {
dispatch(add1())
},2000)
}
}
const mapStateToProps = (state) => {
return { count: state.n }
}
const mapDispatchToProps = (dispatch) => {
return bindActionCreators({add1, add2, minus, oddAdd, delay2s},dispatch)
export default connect(
mapStateToProps,
mapDispatchToProps
)(App)