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帮助我们返回了一个新的对象。
九、通过中间件加一些功能
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!