状态数据管理简单使用(context、mobx、redux)

1,402 阅读7分钟

Context

创建 Context 对象

// context.ts
const MyContext = React.createContext(defaultValue);

提供 Context 对象

import MyContext from './context';

class APP extends React.Component {
  construct(props) {
    super(props);

    // value 值为对象时,状态提升至 state,防止父组件刷新引起子组件不必要的渲染
    this.state = {
      theme: themes.light,
      toggleTheme: this.toggleTheme,
    };
    
    this.toggleTheme = this.toggleTheme.bind(this);
  }
  
  // 改变主题色方法
  toggleTheme () {
    this.setState(state => ({
      theme: state.theme === themes.dark
      ? themes.light
      : themes.dark,
    }));
  }

  render() {
    return (
      <MyContext.Provider value={this.state}>
        {this.props.children}
      </MyContext.Provider>
    )
	}
}

订阅 Context 对象

// 方法1:
class MyClass extends React.Component {
  componentDidMount() {
    let value = this.context;
    /* 在组件挂载完成后,使用 MyContext 组件的值来执行一些有副作用的操作 */
  }

  render() {
    let value = this.context;
    /* 基于 MyContext 组件的值进行渲染 */
  }
}

MyClass.contextType = MyContext;


// 方法2: public class fields 语法【实验性语法】
class MyClass extends React.Component {
  static contextType = MyContext;
 
  render() {
    let value = this.context;
    /* 基于这个值进行渲染工作 */
  }
}

消费 Context 对象

import { MyContext } from './context';

function ThemeTogglerButton() {
  // Theme Toggler 按钮不仅仅只获取 theme 值,它也从 context 中获取到一个 toggleTheme 函数
  return (
    <MyContext.Consumer>
      {(context) => {
        const {theme, toggleTheme} = context;

        return (
          <button	onClick={toggleTheme}
            style={{backgroundColor: theme.background}}
          >
            Toggle Theme
          </button>
        )
      }}
    </MyContext.Consumer>
  );
}

export default ThemeTogglerButton;

更多详细使用:

React Context 基本了解

React Hooks

Mobx

创建 observable 对象

说明:

  • @observable 表示数据可监控, 是全局数据
  • @action 表示修改全局共享数据的方法

RootStore

// mobx/index.ts
import { observable, action } from "mobx";

class RootStore {
  // 手机号码
  @observable mobile = '';
  // token
  @observable token = '';

  // 设置数据信息
  @action setUserInfo(mobile, token) {
    this.mobile = mobile;
    this.token = token;
  }

  // 清除数据信息
  @action clearUserInfo() {
    this.mobile = '';
    this.token = '';
  }
}

export default new RootStore();

UserStore

// mobx/userStore.ts
import { observable, action } from "mobx";

class UserStore {
  // 用户信息对象
  @observable user = {};

  // 设置用户信息
  @action setUser(user) {
    this.user = user;
  };

  // 清除用户信息
  @action clearUser() {
    this.user = {};
  }
}

export default new UserStore();

提供 observable 对象

import React from 'react';
import { Provider} from 'mobx-react';
import RootStore from './mobx';
import UserStore from './mobx/userStore'

const APP = (props) => {
  return (
  	<Provider RootStore={RootStore} UserStore={UserStore}>
     	{props.children}
    </Provider>
  )
}

注入、订阅 observable 对象

  • @inject("RootStore") 注入 observable 对象,获取全局数据 RootStore

  • @observer 监听 observable 对象数据变更,重新渲染更新组件的全局数据

类组件

import React from 'react';
import { inject, observer } from 'mobx-react';

@inject('RootStore') // 注入 RootStore
@inject('UserStore') // 注入 UserStore
@observer
class Demo extends React.Component {
  componentDidMount() {
    // 获取 mobx 内的 RootStore 对象
    console.log(this.props.RootStore);
  }
  
  render() {
    return null
  }
}

函数组件

import React from 'react';
import { inject, observer } from 'mobx-react';

const Demo = (props) => {
  React.useEffect(() => {
    // 获取 mobx 内的 RootStore 对象
    console.log(props.RootStore);
  }, []);
  
  return null
}

// 注入 RootStore
export default inject('RootStore')(observer(Demo));

// 注入 RootStore、 UserStore
export default inject('RootStore')(inject('UserStore')(observer(Demo)));

Redux

Redux 是一个使用叫做 action 的事件来管理和更新应用状态的模式和工具库

创建 reducer 纯函数

返回值: 最新状态值 state

// 初始 state 值
const initialState = [];

// reducer 纯函数
const reducer = (state = initialState, action) => {
    switch(action.type) {
        // 重置 state
        case 'reset':
            return { ...action.state };
        // 更新 state
        case 'update':
            return Object.assign({}, state, action.payload);
        default:
            return state;
    }
}

export default reducer;

说明: 通过分发的 actiontype 类型执行相应的数据更新操作

整合多个 reducer

将多个 reducer 纯函数进行整合并可以进行重新命名

import { combineReducers } from 'redux';
import test1Reducer from './test1Reducer';
import test2Reducer from './test2Reducer';

const reducer = combineReducers({
  test1: test1Reducer,
  test2: test2Reducer
});

创建数据源 store

// store.ts
import reducer from './reducer';

const store = createStore(reducer);

提供 Store

import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'

import App from './App'
import store from './store'

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

connect

connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])

mapStateToProps

mapStateToProps 是一个函数,映射 store 中的数据到组件的 props

mapStateToProps(state, ownProps):stateProps

参数:

  • state Redux 中的 store
  • ownProps 组件自身的 props 属性

返回值: 返回一个 object 对象, 会将该返回值对象与 props 合并

说明:

  • mapStateToProps 会依据参数来订阅 storeownProps 的状态改变(写了该参数就会订阅,没写就不会订阅)

示例

class Demo extends Component {
  static PropTypes = {
    userId: PropTypes.string.isRequired,
    user: PropTypes.object
  };
   
  render(){
    return <div>用户名:{this.props.user.name}</div>
  }
}

const mapStateToProps = (state, ownProps) => {
  // state 是 { userList: [{id: 0, name: '王二'}] }
  return {
    // 查找 store 中 userList 数据源中 id 为 userId 的用户数据传递给 props 
    user: _.find(state.userList, {id: ownProps.userId})
  }
}
 
const MyComp = connect(mapStateToProps)(Demo);

mapDispatchToProps

mapDispatchToProps 用于建立组件跟 store.dispatch 的映射关系, 可以是一个 object,也可以传入函数

mapDispatchToProps(dispatch, ownProps): dispatchProps

参数:

  • dispatch 分发 action 函数
  • ownProps 组件自身的 props 属性

mapDispatchToProps 的三种方式

  1. connect(mapState, null) 不传递第二个参数(或为null) 时
this.props.dispatch({
  type: ChangeColor,
  payload: {
    color: e.target.value
  }
})
  1. connect(mapState, mapDispatch) 的第二个参数为函数
// mapDispatch 为一个函数
const mapDispatchToProps = (dispatch) => ({
  changeColor : (value) => (dispatch({
    type:ChangeColor,
    payload:{
      color: value
    }
  })),
})
  1. connect(mapState,mapDispatch) 的第二个参数为对象
// mapDispatch 为一个对象
const changeColor = (v) => ({
  type: ChangeColor,
  payload:{
    color:v
  }
})

const mapDispatchToProps = {
  changeColor ,
}

Redux 本身也提供了 bindActionCreators 函数,来将 action 包装成直接可被调用的函数。

import { bindActionCreators } from 'redux';

const changeColor = {
  type: ChangeColor,
  payload:{
    color: value
  }
}
 
const mapDispatchToProps = (dispatch, ownProps) => {
  return bindActionCreators({
    changeColor,
  });
}

react-thunk

在创建 store 文件中创建 thunk 中间件

安装

npm install react-thunk

创建 Store 文件

import { createStore, applyMiddleware ,compose } from 'redux’;
import thunk from 'redux-thunk’; 
import reducer from './reducers’; 

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

let store = createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()); 

let store = createStore(reducer, enhancer); 

export default store;

创建 Action Type 文件

export const UPDATE_List = 'update_list';

创建 Action 文件

// actions.ts
// 更新数据信息
const changeArticles = (data) => {
    return {
        type: UPDATE_List,
        data,
    }
}

// redux-thunk actionCreator【创建的 action 函数可以返回一个函数(即可以调用 axios 执行异步请求数据操作)】
export const getDataAxAction = (params) => {   
  return (dispatch, getState) => {     
    axios.get(url, { params })
      .then((res)=>{       
        // res.data.data.list【获取到查询结果后再次分发action,创建action并将异步请求结果赋值给action.value上】      
        dispatch(changeArticles(res.data.data.list));
      }).catch((error)=>{       
        console.log(error);     
    	})
  }; 
}

组件内使用

import React from 'react';
import { getDataAxAction } from './actions';

class Demo extends React.Component {
	
  // 改变 params 来获取最新的 list 数据信息
  updateList() {
    const params = { page: 0, pageSize: 10 };
    this.props.updateList(params);
  }
  
  render(){
    const { list } = this.props;
    return (
    	<div onClick={updateList}>更新 list 数据</div>
    )
  }
}

// 映射 state to props
const mapStateToProps = (state, ownProps) => {
  return {
    list: state.data
  }
}

// 映射 dispatch to props
const mapDispatchToProps = (dispatch) => ({
  updateList : (params) =>(
    dispatch(getDataAxAction(params))
  ),
})

const MyComp = connect(mapStateToProps, mapDispatchToProps)(Demo);

react-saga

在创建 store 文件中创建 saga 中间件

安装

npm install react-saga

创建 store 文件

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

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

let store = createStore(reducer, enhancer); 

//在这里使用 mySagas【在store创建好后,中间件挂好后在使用mySagas】 
sagaMiddleware.run(mySagas);

export default store;

创建 Action Type 文件

export const  UPDATE_PARAMS = 'update_params';

export const  UPDATE_List = 'update_list';

创建 Action 文件

// actions.ts
// 更新 params 数据信息
const updateParams = (params) => {
  return {
    type: UPDATE_PARAMS,
    params,
  }
}

// 更新数据信息
const changeArticles = (data) => {
    return {
        type: UPDATE_List,
        data,
    }
}

创建 mySagas 文件

import { takeEvery, put } from ‘redux-saga/effects’;

//创建 mySagas【generator函数】函数后,在创建 store 仓库中使用 mySagas【saga 中间插件调用 run 方法使用】
function* mySagas(){  
    //监听某个 action, 然后执行第二个参数 gengerator 函数   
    yield takeEvery(UPDATE_PARAMS, getList);   
}

//参数 action 就是对应的 action 对象 
function* getList(action){   
    //发送异步,异步成功后分发 action   
    let res = yield axios.get(url, { params: action.params });   
    //put 就是转发 action,类似于 dispatch,然后执行同步请求执行相应的业务逻辑处理   
    yield put(changeArticles(res.data.list)); 
}

组件内使用

import React from 'react';
import { updateParams } from './actions';

class Demo extends React.Component {
	
  // 改变 params 来获取最新的 list 数据信息
  changeParams() {
    const params = { page: 0, pageSize: 10 };
    this.props.changeParams(params);
  }
  
  render(){
    const { list } = this.props;
    return (
    	<div onClick={changeParams}>更新 list 数据</div>
    )
  }
}

// 映射 state to props
const mapStateToProps = (state, ownProps) => {
  return {
    list: state.data
  }
}

// 映射 dispatch to props
const mapDispatchToProps = (dispatch) => ({
  changeParams : (params) =>(
    dispatch(updateParams(params))
  ),
})

const MyComp = connect(mapStateToProps, mapDispatchToProps)(Demo);

react-thunk 与 react-saga 之间的区别

react-thunk

// redux-thunk actionCreator【创建的 action 函数可以返回一个函数(即可以调用 axios 执行异步请求数据操作)】
export const getDataAxAction = (params) => {   
  return (dispatch, getState) => {     
    axios.get(url, { params })
      .then((res) => {       
        // res.data.data.list【获取到查询结果后再次分发action,创建action并将异步请求结果赋值给action.value上】      
        dispatch(changeArticles(res.data.data.list));
      }).catch((error) => {       
        console.log(error);     
    	})
  }; 
}

说明:

当我们返回的是函数时,store 会帮我们调用这个返回的函数,并且把 dispatch 暴露出来供我们使用。对于 redux-thunk 的整个流程来说,它是等异步任务执行完成之后,我们再去调用 dispatch,然后去 store 去调用 reducer

react-saga

import { takeEvery,put } from ‘redux-saga/effects’;

//创建 mySagas【generator函数】函数后,在创建 store 仓库中使用 mySagas【saga 中间插件调用 run 方法使用】
function* mySagas(){  
    //监听某个 action, 然后执行第二个参数 gengerator 函数   
    yield takeEvery(UPDATE_PARAMS, getList);   
}

//参数 action 就是对应的 action 对象 
function* getList(action){   
    //发送异步,异步成功后分发 action   
    let res = yield axios.get(url, { params: action.params });   
    //put 就是转发 action,类似于 dispatch,然后执行同步请求执行相应的业务逻辑处理   
    yield put(changeArticles(res.data.list)); 
}

说明:

当我们 dispatchaction 类型不在 reducer 中时,redux-saga 的监听函数 takeEvery 就会监听到,等异步任务有结果就执行 put 方法,相当于 dispatch,再一次触发 dispatch对于 redux-saga 的整个流程来说,它是等执行完 actionreducer 之后,判断 reducer 中有没有这个 action