react状态管理全局库详细介绍

708 阅读6分钟

redux

基本使用: 这里将所有代码放到一个文件,真正开发的时候可以分割为多个文件。

import React from 'react'
import { legacy_createStore as createStore } from 'redux'//最新版本不推荐使用createStore,而是使用legacy_createStore代替

const defaultState = { num: 0 }
const reducer = (state = defaultState, action) => {
  switch (action.type) {
    case 'add':
      return { ...state, num: state.num + 1 }
    default:
      return state
  }
}
const store = createStore(reducer)

export default class App extends React.Component {
  constructor() {
    super()
    this.state = { num: store.getState().num }
    store.subscribe(() => {
      this.setState({
        num: store.getState().num,
      })
    })
  }
  render() {
    return (
      <div>
        {this.state.num}
        <br />
        <button
          onClick={() => {
            store.dispatch({
              type: 'add',
            })
          }}
        >
          {' '}
          加
        </button>
      </div>
    )
  }
}

reducer一般可以由多个小的reducer合并而成:

const defaultState = { todos: [], comments: [] };
const todosReducer = (state, action) => {
  switch (action.type) {
    case 'todo_add':
      return [...state, action.payload];
    default:
      return state;
  }
};

const comentsReducer = (state, action) => {
  switch (action.type) {
    case 'comment-add':
      return [...state, action.payload];
    default:
      return state;
  }
};

const rootReducer = (state = defaultState, action) => {
  return {
    todos: todosReducer(state.todos, action),
    comments: comentsReducer(state.comments, action),
  };
};
const store = createStore(rootReducer);

redux使用主要流程:

  • 通过reducer创建一个store仓库
  • 在组件中使用store.getState获取状态
  • 在组件中使用store.dispatch派发修改状态的动作,然后store会将派发的action交给reducer处理,处理之后会返回一个新的状态给store
  • store.subscribe会订阅store的状态,一旦store的状态变化,则会触发订阅函数执行。

react-redux

react-redux是Redux的官方React绑定库,我们可以更方便的使用状态,而不用去手动订阅store状态等。react-redux依赖redux核心库.

基本使用如下:

import React from 'react';
import { legacy_createStore as createStore } from 'redux';
import { Provider, connect } from 'react-redux';

const defaultState = { num: 100 };

const rootReducer = (state = defaultState, action) => {
  switch (action.type) {
    case 'addNum':
      return { num: state.num + 1 };
    default:
      return state;
  }
};
const store = createStore(rootReducer);
export default class App extends React.Component {
  render() {
    return (
      <Provider store={store}>
        <ConnectChild />
      </Provider>
    );
  }
}

class Child extends React.Component {
  render() {
    return (
      <div>
        {this.props.num}
        <br />
        <button
          onClick={() => {
            this.props.addNum();
          }}
        >
          {' '}
          添加
        </button>
      </div>
    );
  }
}
const mapStateToProps = (state) => {
  return {
    num: state.num,
  };
};
const mapDispatchToProps = (dispatch) => {
  return {
    addNum: () => {
      dispatch({
        type: 'addNum',
      });
    },
  };
};

const ConnectChild = connect(mapStateToProps, mapDispatchToProps)(Child);

  • 首先要根据reducer创建一个store数据仓库
  • 根组件使用Provider包含
  • 子类组件使用connect包裹之后,能够方便的dispatch action和获取状态。这里封装了mapStateToProps,其作用是将state相关数据映射到当前组件的props属性上,mapDispatchToProps是将对应的dispatch action方法封装之后映射到当前组件的props上,这样子组件就能方便的获取stroe中的state和发送对应的action
  • 可以看出react-redux岁对之前的getState,dispatch进行了优化,并且redux之前的手动订阅store的操作,react-redux也在内部进行了处理,我们只需要修改状态,状态变化之后,子组件会自动感知到store状态的变化。

函数组件中可使用react-redux提供的useSelector,useDispatch代替connect

import React from 'react';
import { legacy_createStore as createStore } from 'redux';
import { Provider, useSelector, useDispatch } from 'react-redux';

const defaultState = { num: 100 };

const rootReducer = (state = defaultState, action) => {
  switch (action.type) {
    case 'addNum':
      return { num: state.num + action.payload };
    default:
      return state;
  }
};
const store = createStore(rootReducer);
export default class App extends React.Component {
  render() {
    return (
      <Provider store={store}>
        <Child />
      </Provider>
    );
  }
}

function Child() {
  const dispatch = useDispatch();
  const num = useSelector((state) => state.num);
  return (
    <div>
      {num}
      <br />
      <button
        onClick={() => {
          dispatch({
            type: 'addNum',
            payload: 10,
          });
        }}
      >
        数字加10
      </button>
    </div>
  );
}

@reduxjs/toolkit

这是一个简化react-redux使用的一个库,这个库需要结合react-redux库一起使用。

1,定义store

import { configureStore } from '';
import counterReducer from './couter';
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './couter';

const store = configureStore({
  reducer: {
    counterReducer,
  },
});

export default store;

定义store是使用@reduxjs/toolkit包的configureStore进行创建。

2,定义reducer

import { createSlice } from '@reduxjs/toolkit';

const counter = createSlice({
  name: 'counterspace',
  initialState: {
    num: 0,
  },
  reducers: {
    add(state, action) {
      state.num += action.payload;
    },
  },
});

export const { add } = counter.actions;
export default counter.reducer;

createSlice对状态进行管理,返回值包括actions和reducer.导出的reducer用于store创建,action可用于用户派发action.

3,使用状态和修改store状态

import React from 'react';
import { Provider, useDispatch, useSelector } from 'react-redux';

import { add } from './couter';
import store from './store';

export default function App() {
  return (
    <Provider store={store}>
      <Child />
    </Provider>
  );
}

function Child() {
  const dispatch = useDispatch();
  const num = useSelector((state) => state.counterReducer.num);//需要注意,这里counterReducer使用的是store中注册的reducer名称。
  return (
    <div>
      {num}
      <button
        onClick={() => {
          dispatch(add(5));
        }}
      >
        添加剂
      </button>
    </div>
  );
}

异步处理方式1: RTK内置了thunk插件,可以直接处理异步请求

// 内置了thunk插件,可以直接处理异步请求
export const asyncIncrement = (payload) => (dispatch) => {
 setTimeout(() => {
 dispatch(increment(payload));
  }, 2000);
};

调用:
 dispatch(asyncIncrement({ step: 1 }));

异步处理方式2:

创建异步action createAsyncThunk方法可以创建一个异步的action,这个方法被执行的时候会有三个( pending(进行中) fulfilled(成功) rejected(失败))状态。可以监听状态的改变执行不同的操作。以下代码示例中使用到了extraReducers创建额外的action对数据获取的状态信息进行监听。

import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import { increment } from './counterSlice';
// 发起网络请求获取数据
const loadMoviesAPI = () =>
 fetch(
 'https://pcw-api.iqiyi.com/search/recommend/list?channel_id=1&data_type=1&mode=11&page_id=2&ret_num=48'
  ).then((res) => res.json());
​
// 这个action是可以直接调用的,用来处理异步操作获取数据
export const loadData = createAsyncThunk('movie/loadData', async () => {
 const res = await loadMoviesAPI();
 return res; // 此处的返回结果会在 .fulfilled中作为payload的值
});
​
export const movieSLice = createSlice({
 ....
 // 可以额外的触发其他slice中的数据关联改变
 extraReducers: {
    [loadData.fulfilled](state, { payload }) {
 console.log(payload);
 state.list = payload.data.list;
    },
    [loadData.rejected](state, err) {
 console.log(err);
    },
    [loadData.pending](state) {
 console.log('进行中');
    },
  },
});
​
export const { loadDataEnd } = movieSLice.actions;
export default movieSLice.reducer;

总结:

  • Provider依旧使用react-redux中的方式。
  • 不再使用connect来获取状态以及获取dispactch action
  • 使用useDispatch方法dispatch相关action,dispatch(add(5))只需要在方法中传入参数即可。
  • 使用useSelector获取store中的状态
  • SKT修改状态的时候不用进行拷贝,可以直接在原来状态基础上进行修改。

@rematch/core

rematch是对redux的二次封装,简化了redux是使用,极大的提高了开发体验。rematch仅仅是对redux的封装,没有依赖redux-saga,也没有关联react,因此其可以用在其他的视图库中,如vue,react等。

基本使用如下:

1.model定义

const count = {
  state: {
    num: 1,
  },
  reducers: {
    increment(state, num) {
      return {
        ...state,
        num: state.num + num,
      };
    },
  },
  //第一种effects写法
  // effects: {
  //   async incrementAsync(ånum1, rootState, num2) {
  //     await new Promise((resolve) => setTimeout(resolve, 2000));
  //     this.increment(num1);
  //   },
  // },
  //第二种effects写法
  effects: (dispatch) => ({
    async incrementAsync(num1, rootState, num2) {
      await new Promise((resolve) => setTimeout(resolve, 2000));
      dispatch.count.increment(num1);
    },
  }),
};

export default count;
  • 项目中可能会有多个模块状态,使用modal可以将每个模块的状态,reducer,effects分离开
  • state为状态,reducer是一些同步修改状态的方法,有副作用的则放在effects中
  • 有两种effects的写法如上。 2,store创建
import { init } from '@rematch/core';
import count from './model/count';

const store = init({
  models: {
    count,
  },
});

export default store;

使用@rematch/core的init创建store,models属性存放我们定义的所有models

获取状态和修改状态:

import React, { Component } from 'react';
import { connect } from 'react-redux';

class App extends Component {
  handleClick = () => {
    const { countDispatch } = this.props;
    countDispatch.incrementAsync(10);
  };

  render() {
    const { countState } = this.props;
    return (
      <div>
        当前num:{countState.num}
        <button onClick={this.handleClick}>点我num加10</button>
      </div>
    );
  }
}

const mapStateToProps = (state) => ({
  countState: state.count,
});
const mapDispatchToProps = (dispatch) => ({
  countDispatch: dispatch.count,
});
export default connect(mapStateToProps, mapDispatchToProps)(App);

在类组件中依然使用react-redux的高阶组件connect,mapDispatchToProps,mapStateToProps来达到获取状态和修改状态的需求。

import React from 'react';
import { useDispatch, useSelector } from 'react-redux';

function App() {
const dispatch = useDispatch();
const countState = useSelector((state) => state.count);

return (
  <div>
    当前num为:{countState.num}
    <button
      className="App"
      onClick={() => {
        dispatch.count.increment(10);
      }}
    >
      点我num加10
    </button>
  </div>
);
}

函数组件则直接使用react-redux提供的hooks(useDispatch,useSelector)即可获取对应的状态和dispatch对象。

recoil

详细讲解移步:juejin.cn/post/711118…