React—11—redux
一、redux概念
Redux的三大原则

二、在非脚手架中使用redux
redux是一个单独的库,所以可以在普通项目、vue、react中使用。
三、react中使用redux
我的理解是,createStore时必须有一个reducer函数,
store会主动调用reducer函数返回的state作为初始state;
后续,当我们使用dispatch取修改的时候,redux会主动调用reducer,然后reducer内部去做修改然后返回一个全新的state,
由于createStore是跟reducer强绑定的,所以store的数据也就跟着改变了。
第一:npm install redux
第二:专门建一个store文件夹,去实例化一个store,并且这个store的数据是由reducer提供的。
第三:其他组件可以通过store.getState取获取store数据的初始值
第四:想要修改store里的值,需要用到store.dispatch({type:'xxxxxx', params);
第五:当我们dispatch时,redux会自动帮助我们调用reducer函数,所以我们需要在reducer函数里做处理
reducer函数有两个参数,一个是旧state,一个是本次提出dispatch的action对象。
经过处理后,返回一个新的state(记住,reducer是一个纯函数,所以不要修改原旧state,要返回一个新的state。)
第六: redux把旧的state更新成reducer新返回的state
第七:通知所有订阅过(store.subcribe())的组件
import { createStore } from 'redux';
import * as actionTypes from './constants';
// 定义常量
export const ADD_NUMBER = 'add_number';
export const SUB_NUMBER = 'sub_number';
// 定义action
export const addNumberAction = num => ({
type: ADD_NUMBER,
num
});
export const subNumberAction = num => ({
type: SUB_NUMBER,
num
});
// 定义reducer函数
const initialState = {
counter: 0
};
export function reducer(state = initialState, action) {
switch (action.type) {
case actionTypes.ADD_NUMBER:
return { ...state, counter: state.counter + action.num };
case actionTypes.SUB_NUMBER:
return { ...state, counter: state.counter - action.num };
default:
return state;
}
}
// 实例化store
const store = createStore(reducer);
export default store;
export class App extends PureComponent { constructor() { super(); this.state = { counter: store.getState().counter }; } componentDidMount() { store.subscribe(() => this.setState({ counter: store.getState().counter })); } render() { const { counter } = this.state; return ( <div> <h1> App {counter} <Home></Home> <Profile></Profile> </h1> </div> ); } } export default App;
import React, { PureComponent } from 'react'
import store from "../store"
import { addNumberAction } from '../store/counter'
export class Home extends PureComponent {
constructor() {
super()
this.state = {
counter: store.getState().counter,
}
}
componentDidMount() {
store.subscribe(() => {
const state = store.getState()
this.setState({ counter: state.counter })
})
}
addNumber(num) {
store.dispatch(addNumberAction(num))
}
render() {
const { counter } = this.state
return (
<div>
<h2>Home Counter: {counter}</h2>
<div>
<button onClick={e => this.addNumber(1)}>+1</button>
<button onClick={e => this.addNumber(5)}>+5</button>
<button onClick={e => this.addNumber(8)}>+8</button>
</div>
</div>
)
}
}
export default Home
四、使用库react-redux库简化我们的代码
1.npm install react-redux
2.在index.js里引入库提供的provide组件,然后把仓库store放入。
import { Provider } from "react-redux" import store from "./store" const root = ReactDOM.createRoot(document.getElementById('root')); root.render( // <React.StrictMode> <Provider store={store}> <App /> </Provider> // </React.StrictMode> );
3.我们自己的组件想使用仓库store的数据,也不用store.getState().xxx了,这样很麻烦。
我们先导入react-redux库的connect函数。
通过connect(par1,par2)(Abount)代表,我们将导出一个由react-redux管理的About组件。
其中par1表示要传入一个函数,这个函数的参数为仓库store的state,这个函数的返回值将会加在About组件的props上, 即 <About diyCounter:state:counter></About>
其中par2表示要传入一个函数,这个函数的参数为仓库store的dispatch,这个函数的返回值将会加在About组件的props上, 即 <About addNumber:addNumber subNUmber:subNumber></About>
import React, { PureComponent } from 'react';
import { connect } from 'react-redux';
import { addNumberAction, subNumberAction } from '../store/counter';
export class About extends PureComponent {
render() {
const { diyCounter, addNumber, subNumber } = this.props;
return (
<div>
<h1>About:{diyCounter}</h1>
<div>
<button onClick={e => addNumber(1)}>+1</button>
<button onClick={e => addNumber(5)}>+5</button>
<button onClick={e => subNumber(1)}>-1</button>
<button onClick={e => subNumber(5)}>-5</button>
</div>
</div>
);
}
}
const mapStateToProps = state => ({
diyCounter: state.counter
});
const mapDispatchToProps = dispach => ({
addNumber(num) {
dispach(addNumberAction(num));
},
subNumber(num) {
dispach(subNumberAction(num));
}
});
export default connect(mapStateToProps, mapDispatchToProps)(About);
五、使用react-redux库和redux-thunk库进行异步操作数据
这个redux-thunk库主要是帮我们做了一个增强:本来dispatch()只可以传入一个对象{type:'xxx', par},现在增强后,还可以给dispatch里传入一个函数,这样就方便我们进行异步操作。
1.npm install redux-thunk;
2.在createStore时,除了传入reducer,也传入这个thunk;
3.在mapDispatchToProps里的dispatch函数,可以传入一个函数,这个函数里进行异步操作。
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { reducer } from './reducer.js';
const store = createStore(reducer, applyMiddleware(thunk));
export default store;
import React, { PureComponent } from 'react';
import { connect } from 'react-redux';
import { fetchRecommendDdataAction } from '../store/counter';
export class Recommend extends PureComponent {
componentDidMount() {
this.props.fetchRecommendDdata();
}
render() {
const { recommend } = this.props;
return (
<div>
<h1>Recommend</h1>
<ul>
{recommend.map(e => {
return <li key={e.title}> {e.title}</li>;
})}
</ul>
</div>
);
}
}
const mapStateToProps = state => ({ recommend: state.recommend });
const mapDispatchToProps = dispach => ({
fetchRecommendDdata() {
dispach(fetchRecommendDdataAction());
}
});
export default connect(mapStateToProps, mapDispatchToProps)(Recommend);
import { ADD_NUMBER, SUB_NUMBER, GET_RECOMMEND_DATA } from './constants';
import axios from 'axios';
export const fetchRecommendDdataAction = () => {
return dispatch => {
axios.get('http://123.207.32.32:8000/home/multidata').then(res => {
dispatch({ type: GET_RECOMMEND_DATA, recommendData: res.data.data.recommend.list });
});
};
};
六、使用react-redux库和redux-toolkit库进行操作

我们目前redxu写法有点麻烦,可以使用redux-toolkit简化我们的操作,而且这这个库里面也内置redux-thunk,不需要额外安装了。
第一步:npm i @reduxjs/toolkit
store.js
import { configureStore } from '@reduxjs/toolkit';
import counterSlice from './features/counter';
import recommendSlice from './features/recommend';
const store = configureStore({
reducer: {
counterSlice,
recommendSlice
}
});
export default store;
recucer函数
import { createSlice } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counterSlice',
initialState: {
counter: 0
},
reducers: {
addNumberAction(state, action) {
// 使用redux-toolkit,这里无需返回一个新的对象,库内部会帮我们返回一个新的对象,我们这里直接修改原state即可。
state.counter += action.payload;
},
subNumberAction(state, action) {
state.counter -= action.payload;
}
}
});
export const { addNumberAction, subNumberAction } = counterSlice.actions;
export default counterSlice.reducer;
Home.jsx
这里面要注意的是,dispatch的action,不需要我们自己定义了,reducer.js里定义的action可以通过export const { addNumberAction, subNumberAction } = counterSlice.actions;导出,
然后组件里直接使用即可,库内部会帮助我们做好匹配。
import React, { PureComponent } from 'react';
import { connect } from 'react-redux';
import { addNumberAction } from '../store/features/counter';
export class Home extends PureComponent {
addNumber(num) {
this.props.addNumber(num);
}
render() {
const { counter } = this.props;
console.log('%c [ counter ]-12', 'font-size:13px; background:pink; color:#bf2c9f;', counter)
return (
<div>
<h2>Home Counter: {counter}</h2>
<div>
<button onClick={e => this.addNumber(1)}>+1</button>
<button onClick={e => this.addNumber(5)}>+5</button>
<button onClick={e => this.addNumber(8)}>+8</button>
</div>
</div>
);
}
}
const mapStateToProps = state => ({
counter: state.counterSlice.counter
});
const mapDispatchToProps = dispatch => ({
addNumber(num) {
dispatch(addNumberAction(num));
}
});
export default connect(mapStateToProps, mapDispatchToProps)(Home);
如果想异步操作,需要这样写:
在reducers外面定义一个action,createAsyncThunk就是redux-toolkit里内置了reduxthunk的功能。
fetchRecommendDdataAction = createAsyncThunk()
这个函数createAsyncThunk()第一个参数传递名字,第二个参数要传递一个回调函数,回调函数的payload是组件调用时可以传递的参数,store时仓库实例,可以通过store.dispatch(changeRecommendAction(res.data.data.recommend.list))继续调用reducer里的方法
取修改仓库。
或者用extraReducers修改,也可以。
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';
export const fetchRecommendDdataAction = createAsyncThunk('fetch/recommendData', async (payload, store) => {
const res = await axios.get('http://123.207.32.32:8000/home/multidata');
// 方式一:直接调用reducers里的action进行改变
// store.dispatch(changeRecommendAction(res.data.data.recommend.list));
// 方式二: 这里return数据,然后在extraReducers里处理
return res.data.data;
});
const recommendSlice = createSlice({
name: 'recommendSlice',
initialState: {
recommend: [{ title: '是全全美' }]
},
reducers: {
changeRecommendAction(state, action) {
// 使用redux-toolkit,这里无需返回一个新的对象,库内部会帮我们返回一个新的对象,我们这里直接修改原state即可。
state.recommend = action.payload;
}
},
extraReducers(builder) {
builder.addCase(fetchRecommendDdataAction.pending, (state, action) => {
}).addCase(fetchRecommendDdataAction.fulfilled, (state, action) => {
state.recommend = action.payload.recommend.list;
})
}
});
export const { changeRecommendAction } = recommendSlice.actions;
export default recommendSlice.reducer;
七、react-redux的connect()的原理

八、redux的数据不可以变性
为什么我们可以在reduces中,直接通过state.counter += action.num去改变值,而不是返回一个新的对象?
因为库内部使用immerjs帮助我们返回了一个新的对象。

九、通过中间件加一些功能




浙公网安备 33010602011771号