Redux
Redux是JavaScript应⽤的状态容器。它保证程序⾏为⼀致性且易于测试。
安装
npm i redux -S
过程:
- 需要一个store来存储数据
- store里的
reducer
初始化state并定义state修改规则 - 通过dispatch一个action来提交对数据的修改
- action提交到reducer函数里,根据传入的action的type,返回的新的state
redux概念:
- createStore: 创建store
- reducer: 初始化、修改状态函数
- getState:获取状态值
- dispatch 提交更新
- subscribe 变更订阅
例子:
创建store:
// src/store/store.js
import {createStore} from 'redux'
const counterReducer = (state = 0,action) =>{
switch(action.type){
case "add":
return state+1;
case 'minus':
return state-1;
default:
return state;
}
}
const store = createStore(counterReducer);
export default store;
// reduxpage.js
import React,{Component} from 'react'
import store from '../store/reduxStore'
export default class ReduxPage extends Component{
componentDidMount(){
store.subscribe(()=>{ // 订阅更新,当数据发生变化时
this.forceUpdate(); // 强制更新
})
}
add= ()=>{
store.dispatch({type:'add'})
}
minus=()=>{
store.dispatch({type:'minus'});
}
render(){
return(
<div>
<h1>reduxPage</h1>
<p>{store.getState()}</p>
<button onClick={this.add}>add</button>
<button onClick={this.minus}>minus</button>
</div>
)
}
}
react-redux
使用redux的话,需要重新调用render,获取数据要通过getState,为此引入react-redux。
安装
npm install react-redux --save
react-redux提供了两个api
- Provider 为其他组件提供store
- connect 为组件提供数据的变更方法
创建两个reducer:
// counterReducer.js
const counterReducer = (state = 0,action) =>{
switch(action.type){
case "add":
return state+1;
case 'minus':
return state-1;
default:
return state;
}
}
export default counterReducer;
// multipleReducer.js
const multipleReducer = (state = 1,action) =>{
switch(action.type){
case "double":
return state*2;
default:
return state;
}
}
export default multipleReducer;
// reactRedux.js
import {createStore,combineReducers} from 'redux'
import counterReducer from './counterReducer'
import multipleReducer from './multipleReducer'
const store = createStore(combineReducers({ // combineReducers 结合多个reducers
counter:counterReducer,
multi:multipleReducer
}))
export default store;
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import Page from './pages3/page'
import store from './store/reactRedux'
import {Provider} from 'react-redux'
const jsx = (
<div>
<Provider store={store}>
<Page/>
</Provider>
</div>
)
ReactDOM.render(
jsx,
document.getElementById('root')
);
获取状态数据
// page.js
import React,{Component} from 'react'
import {connect} from 'react-redux'
class ReactReduxPage extends Component{
render(){
console.log(this.props) // props中可以获取到connect方法中所有映射过来的值跟方法
const {num,add,minus} = this.props;
return (
<div>
<p>{num}</p>
<button onClick={add}>add</button>
<button onClick={minus}>minus</button>
</div>
)
}
}
const mapStateToProps = state =>{ // 映射state到props中
return {
num:state.multi,
numadd:state.counter
}
}
const mapDispatchToProps={ // 映射dispatch到props中
add:()=>{
return {
type:'add'
}
},
minus:()=>{
return {
type:'minus'
}
}
}
export default connect( // 获取provider中传过来的值
mapStateToProps,
mapDispatchToProps
)(ReactReduxPage)
异步
Redux只是个纯粹的状态管理器,默认只⽀持同步,实现异步任务⽐如延迟,⽹络请求,需要中间件的⽀持,安装redux-thunk
npm i redux-thunk -S
在没有使用中间件的时候调用异步会报错。
asyncAdd:()=>{
setTimeout(()=>{
return {
type:'double'
}
})
}
使用中间件
// reactRedux.js
import {createStore,combineReducers,applyMiddleware} from 'redux'
import counterReducer from './counterReducer'
import multipleReducer from './multipleReducer'
import thunk from 'redux-thunk'
const store = createStore(combineReducers({
counter:counterReducer,
multi:multipleReducer
}),applyMiddleware(thunk))
export default store;
// page.js
asyncAdd:()=>dispatch=>{
setTimeout(()=>{
dispatch({type:'double'})
},1000)
}
Redux原理
核心:
- state 状态
- getState 获取状态
- dispatch 更新
- subscribe 订阅变更
实现原理
export function createStore(reducer,enhancer){
let currentState = undefined; //当前状态
let currentListeners = [];
function getState(){ // 获取当前状态
return currentState;
}
function subscribe(listener){ // 订阅变更
currentListeners.push(listener)
}
function dispatch(action){ // 变更状态
currentState = reducer(currentState,action)
currentListeners.forEach(v=>v());
return action;
}
return {getState,subscribe,dispatch}
}
// store.js
import {createStore} from '../lRedux'
const counterReducer = (state = 0,action) =>{
switch(action.type){
case "add":
return state+1;
case 'minus':
return state-1;
default:
return state;
}
}
const store = createStore(counterReducer);
export default store;
// page.js
import React,{Component} from 'react'
import store from '../store/store'
export default class LReduxPage extends Component{
componentDidMount(){
store.subscribe(()=>{
this.forceUpdate();
})
}
add= ()=>{
console.log(store.dispatch({type:'add'}))
}
minus=()=>{
store.dispatch({type:'minus'});
}
render(){
console.log('store',store);
return(
<div>
<h1>reduxPage</h1>
<p>{store.getState()}</p>
<button onClick={this.add}>add</button>
<button onClick={this.minus}>minus</button>
</div>
)
}
}
中间件的实现
重点就是 实现函数序列执行
export function createStore(reducer,enhancer){
if(enhancer){
return enhancer(createStore)(reducer) // 增强store,将createStore传进去
}
let currentState = undefined;
let currentListeners = [];
function getState(){
return currentState;
}
function subscribe(listener){
currentListeners.push(listener)
}
function dispatch(action){
currentState = reducer(currentState,action)
currentListeners.forEach(v=>v());
return action;
}
dispatch({type:'default'})
return {getState,subscribe,dispatch}
}
export function applyMiddleware(...middlewares){ // 使用中间件
return createStore=>(...args)=>{ // 接受createStore作为参数
const store = createStore(...args); // 获取到reducer对应的store
let dispatch = store.dispatch; // 获取dispatch方法
const midApi = { // 中间件的api
getState:store.getState, // 获取state
dispatch:(...args)=>dispatch(...args) // 获取reducer对应的store中的dispatch方法
}
const middlewareChain = middlewares.map(middleware=>middleware(midApi)) // 传递给所有的中间件
dispatch = compose(...middlewareChain) // 组合所有的middleware
(store.dispatch) // 将dispatch传进组合后的方法中
console.log({...store,dispatch})
return {...store,dispatch} // 返回整合后的store
}
}
export function compose(...funcs){ // 组合
if(funcs.length === 0){
return arg => arg
// return ()=>{} // 返回一个函数
}
if(funcs.length === 1){
return funcs[0]
}
return funcs.reduce((a,b)=>(...args)=>a(b(...args)))
}
// store.js
import {createStore,applyMiddleware} from '../lRedux'
const counterReducer = (state = 0,action) =>{
switch(action.type){
case "add":
return state+1;
case 'minus':
return state-1;
default:
return state;
}
}
const store = createStore(counterReducer,applyMiddleware(logger)); // 使用了中间件
export default store;
function logger(){
return dispatch => action =>{
action.type && console.log(`${action.type} 已经执行了`);
return dispatch(action)
}
}
redux-thunk 原理
thunk增加了处理函数型action的能⼒。
// store.js
import {createStore,applyMiddleware} from '../lRedux'
const counterReducer = (state = 0,action) =>{
switch(action.type){
case "add":
return state+1;
case 'minus':
return state-1;
default:
return state;
}
}
const store = createStore(counterReducer,applyMiddleware(thunk,logger));
console.log(store)
export default store;
function logger(){
return dispatch => action =>{
action.type && console.log(`${action.type} 已经执行了`);
return dispatch(action)
}
}
function thunk({getState}){
return dispatch=>action=>{
if(typeof action === 'function'){ // 传入函数
return action(dispatch,getState)
}else{
return dispatch(action)
}
}
}
// page.js
asyncAdd=()=>{
store.dispatch(dispatch=>{ // dispatch中传入函数
setTimeout(()=>{
dispatch({type:'add'})
},1000)
})
}
react-redux原理
核心:
- 高阶函数connect,可以根据传⼊状态映射规则函数和派发器映射规则函数映射需要的属性,可以处理变更检测和刷新任务。
- Provider组件传递store
实现react-redux:
// lreact-redux.js
import React from 'react'
import PropTypes from 'prop-types'
import {bindActionCreators} from './lRedux'
// connect()() // 传入两个参数,将state、dispatch映射到props中,返回一个传入组件的函数
export const connect = (
mapStateToProps = state=>state,
mapDispatchToProps = {}
) => (WrapComponent) => {
return class ConnectComponent extends React.Component{
static contextTypes={ // class组件声明静态contextTypes可以获取上下文Context
store:PropTypes.object
}
constructor(props,context){ // 传入props,context
super(props,context);
this.state={
props:{}
}
}
componentDidMount(){
const {store} = this.context // 从上下文中获取store
store.subscribe(()=>this.update()) // 订阅更新
this.update() // 执行更新
}
update(){
const {store} = this.context;
const stateProps = mapStateToProps(store.getState()); // 获取state
const dispatchProps = bindActionCreators(mapDispatchToProps,store.dispatch) // 获取dispatch
console.log(dispatchProps)
this.setState({
props:{ // 往props传入所有的值
...this.state.props,
...stateProps,
...dispatchProps
}
})
}
render(){ // 重新渲染传入的组件,并传入所有的props
return <WrapComponent {...this.state.props} />
}
}
}
export class Provider extends React.Component{ // 提供store
static childContextTypes={
store:PropTypes.object
}
getChildContext(){
return {store:this.store}
}
constructor(props,context){
super(props,context)
this.store = props.store // 传入组件的store <Provider store={store} />
}
render(){
return this.props.children
}
}
lredux中加入解析action的方法
function bindActionCreator(creator,dispatch){
return (...args) => dispatch(creator(...args))
}
// creators 的格式
// add:()=> {return {type:'add'}}
/**
* asyncAdd:()=>dispatch=>{
setTimeout(()=>{
dispatch({type:'double'})
},1000)
}
*/
export function bindActionCreators(creators,dispatch){
return Object.keys(creators).reduce((ret,item)=>{
ret[item]= bindActionCreator(creators[item],dispatch)
console.log(ret[item])
return ret
},{})
}
最后在index.js中引入Provider
import {Provider} from './nativeRedux/lreact-redux'
...
<Provider store={store}>
<Page />
</Provider>