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…