一文詳解React?Redux使用方法
一、理解JavaScript純函數(shù)
1.1 純函數(shù)的概念
純函數(shù)的維基百科定義:
- 在程序設(shè)計(jì)中,若一個(gè)函數(shù)符合以下條件,那么這個(gè)函數(shù)被稱為純函數(shù)
- 此函數(shù)在相同的輸入值時(shí),需產(chǎn)生相同的輸出
- 函數(shù)的輸出和輸入值以外的其他隱藏信息或狀態(tài)無(wú)關(guān),也和由I/O設(shè)備產(chǎn)生的外部輸出無(wú)關(guān)
- 該函數(shù)不能有語(yǔ)義上可觀察的函數(shù)副作用,諸如“觸發(fā)事件”,使輸出設(shè)備輸出,或更改輸出值以外物件的內(nèi)容等
純函數(shù)概念,總結(jié)如下:
- 確定的輸入,一定會(huì)產(chǎn)生確定的輸出
- 函數(shù)在執(zhí)行過(guò)程中,不能產(chǎn)生副作用
案例(數(shù)組的兩個(gè)方法):
- slice:slice截取數(shù)組時(shí)不會(huì)對(duì)原數(shù)組進(jìn)行任何操作,而是生成一個(gè)新的數(shù)組
- splice:splice截取數(shù)組, 會(huì)返回一個(gè)新的數(shù)組,也會(huì)對(duì)原數(shù)組進(jìn)行修改
1.2 副作用概念的理解
什么是副作用?
- 副作用(side effect)表示在執(zhí)行一個(gè)函數(shù)時(shí),除了返回函數(shù)值之外,還對(duì)調(diào)用函數(shù)產(chǎn)生了附加的影響,比如修改了全局變量,修改參數(shù)或者改變外部的存儲(chǔ)
純函數(shù)在執(zhí)行的過(guò)程中就是不能產(chǎn)生這樣的副作用:
- 副作用往往是產(chǎn)生bug的 “溫床”
1.3 純函數(shù)在函數(shù)式編程的重要性
為什么純函數(shù)在函數(shù)式編程中非常重要呢?
- 可以安心的編寫和安心的使用
- 在寫的時(shí)候保證了函數(shù)的純度,只是單純實(shí)現(xiàn)自己的業(yè)務(wù)邏輯即可,不需要關(guān)心傳入的內(nèi)容是如何獲得的或者依賴其他的外部變量是否已經(jīng)發(fā)生了修改
- 在用的時(shí)候,確定的輸入內(nèi)容不會(huì)被任意篡改,并且自己確定的輸入,一定會(huì)有確定的輸出
- React中就要求我們無(wú)論是函數(shù)還是class聲明一個(gè)組件,這個(gè)組件都必須像純函數(shù)一樣,保護(hù)它們的
props
不被修改 - 在下面的redux中,reducer也被要求是一個(gè)純函數(shù)
二、Redux的核心思想
2.1 為什么需要 Redux
JavaScript開發(fā)的應(yīng)用程序變得越來(lái)越復(fù)雜:
- JavaScript需要管理的狀態(tài)越來(lái)越多,越來(lái)越復(fù)雜
- 這些狀態(tài)包括服務(wù)器返回的數(shù)據(jù)、緩存數(shù)據(jù)、用戶操作產(chǎn)生的數(shù)據(jù)等等,也包括一些UI的狀態(tài),比如某些元素是否被選中,是否顯示加載動(dòng)效,當(dāng)前分頁(yè)
管理不斷變化的state是非常困難的:
- 狀態(tài)之間相互會(huì)存在依賴,一個(gè)狀態(tài)的變化會(huì)引起另一個(gè)狀態(tài)的變化,View頁(yè)面也有可能會(huì)引起狀態(tài)的變化
- 當(dāng)應(yīng)用程序復(fù)雜時(shí),state在什么時(shí)候,因?yàn)槭裁丛蚨l(fā)生了變化,發(fā)生了怎么樣的變化,會(huì)變得非常難以控制和追蹤
React是在視圖層幫助我們解決了DOM的渲染過(guò)程,但是State依然是留給我們自己來(lái)管理:
- 無(wú)論是組件定義自己的state,還是組件之間的通信通過(guò)props進(jìn)行傳遞;也包括通過(guò)Context進(jìn)行數(shù)據(jù)之間的共享
- React主要負(fù)責(zé)幫助我們管理視圖,state如何維護(hù)最終還是我們自己來(lái)決定
- Redux就是一個(gè)幫助我們管理State的容器:Redux是JavaScript的狀態(tài)容器,提供了可預(yù)測(cè)的狀態(tài)管理
- Redux除了和React一起使用之外,它也可以和其他界面庫(kù)一起來(lái)使用(比如Vue、小程序),并且它非常?。òㄒ蕾囋趦?nèi),只有2kb)
2.2 Redux的核心概念
2.2.1 store
可以定義一些初始化的數(shù)據(jù),通過(guò) reducer 傳入
2.2.2 action
- store 中數(shù)據(jù)的變化,必須通過(guò)派發(fā)(dispatch)action來(lái)更新
- action是一個(gè)普通的JavaScript對(duì)象,用來(lái)描述這次更新的type和content
2.2.3 reducer
將傳入的state和action結(jié)合起來(lái)生成一個(gè)新的state
2.3 Redux的三大原則
2.3.1 單一數(shù)據(jù)源
- 整個(gè)應(yīng)用程序的state被存儲(chǔ)在一顆object tree中,并且這個(gè)object tree只存儲(chǔ)在一個(gè) store 中
- Redux并沒(méi)有強(qiáng)制讓我們不能創(chuàng)建多個(gè)Store,但是那樣做并不利于數(shù)據(jù)的維護(hù)
- 單一的數(shù)據(jù)源可以讓整個(gè)應(yīng)用程序的state變得方便維護(hù)、追蹤、修改
2.3.2 State是只讀的
- 唯一修改State的方法是觸發(fā)action,不要試圖在其他地方通過(guò)任何的方式來(lái)修改State
- 這樣就確保了View或網(wǎng)絡(luò)請(qǐng)求都不能直接修改state,它們只能通過(guò)action來(lái)描述自己想要如何修改state
- 這樣可以保證所有的修改都被集中化處理,并且按照嚴(yán)格的順序來(lái)執(zhí)行,所以不需要擔(dān)心race condition(竟態(tài))的問(wèn)題
2.3.3 使用純函數(shù)來(lái)執(zhí)行修改
- 通過(guò)reducer將 舊state和 actions聯(lián)系在一起,并且返回一個(gè)新的State
- 隨著應(yīng)用程序的復(fù)雜度增加,可以將reducer拆分成多個(gè)小的reducers,分別操作不同state tree的一部分
- 但是所有的reducer都應(yīng)該是純函數(shù),不能產(chǎn)生任何的副作用
2.4 Redux 工作流程
建議看完Redux基本使用后再來(lái)看這幅圖:
三、Redux基本使用
注意:以下 3 部分代碼在 node 環(huán)境下
- 需要安裝redux:
npm install redux
補(bǔ)充:node中對(duì)ES6模塊化的支持
node v13.2.0
開始,對(duì)ES6模塊化提供了支持:
node v13.2.0之前,需要進(jìn)行如下操作:
- 在package.json中添加屬性: "type": "module"
- 在執(zhí)行命令中添加如下選項(xiàng):node --experimental-modules src/index.js
node v13.2.0之后,只需要進(jìn)行如下操作:
- 在package.json中添加屬性: "type": "module"
- 注意:導(dǎo)入文件時(shí),需要跟上
.js
后綴名
3.1 創(chuàng)建Store的過(guò)程
定義reducer:必須是一個(gè)純函數(shù),不要直接修改state
createStore 傳入 reducer
const { createStore } = require('redux') // 初始化的數(shù)據(jù) const initialState = { name: '李雷', counter: 100 } // 定義reducer函數(shù):純函數(shù) // 兩個(gè)參數(shù): // 參數(shù)一:store中目前保存的state // 參數(shù)二:本次需要更新的action(dispatch傳入的action) // 返回值:返回值會(huì)作為store之后存儲(chǔ)的state function reducer(state = initialState, action) { switch (action.type) { case 'change_name': return { ...state, name: action.name } case 'add_numer': return { ...state, counter: state.counter + action.num } default: return state } } // 創(chuàng)建store const store = createStore(reducer) module.exports = store
3.2 dispatch派發(fā)action
- store 通過(guò) dispatch 來(lái)派發(fā) action
- 通常會(huì)有 type 屬性,也可以攜帶其他數(shù)據(jù)
const store = require('./store') console.log(store.getState()) // { name: '李雷', counter: 100 } // 修改store中的數(shù)據(jù):必須action const nameAction = { type: 'change_name', name: '韓梅梅' } store.dispatch(nameAction) console.log(store.getState()) // { name: '韓梅梅', counter: 100 } const nameAction2 = { type: 'change_name', name: '夏洛' } store.dispatch(nameAction2) console.log(store.getState()) // { name: '夏洛', counter: 100 } // 修改counter const counterAction = { type: 'add_numer', num: 10 } store.dispatch(counterAction) console.log(store.getState()) // { name: '夏洛', counter: 110 }
3.3 subscribe定位state
store.subscribe()
傳入一個(gè)函數(shù)能夠監(jiān)聽(tīng)數(shù)據(jù)的變化store.subscribe()
會(huì)返回一個(gè)函數(shù),執(zhí)行該函數(shù)取消監(jiān)聽(tīng)
const store = require('./store') const unSubscribe = store.subscribe(() => { console.log('訂閱數(shù)據(jù)的變化:', store.getState()) }) // 修改store中的數(shù)據(jù):必須action store.dispatch({ type: 'change_name', name: '韓梅梅' }) store.dispatch({ type: 'change_name', name: '夏洛' }) // 取消訂閱 unSubscribe() // 修改counter store.dispatch({ type: 'add_numer', num: 10 })
3.4 代碼優(yōu)化
- 優(yōu)化方向:
- action的創(chuàng)建放到一個(gè)函數(shù)中
- 抽取到actionCreators.js文件中
- 所有的字符串常量放到constants.js文件
- reducer函數(shù)和初始化值, 放到reducer.js文件
- index.js中創(chuàng)建store和導(dǎo)出store
示例:
actionCreators.js
const { ADD_NUMBER, CHANGE_NAME } = require("./constants") const changeNameAction = (name) => ({ type: CHANGE_NAME, name }) const addNumberAction = (num) => ({ type: ADD_NUMBER, num }) module.exports = { changeNameAction, addNumberAction }
const ADD_NUMBER = "add_number" const CHANGE_NAME = "change_name" module.exports = { ADD_NUMBER, CHANGE_NAME }
const { CHANGE_NAME, ADD_NUMBER } = require('./constants') // 初始化的數(shù)據(jù) const initialState = { name: '李雷', counter: 100 } function reducer(state = initialState, action) { switch (action.type) { case CHANGE_NAME: return { ...state, name: action.name } case ADD_NUMBER: return { ...state, counter: state.counter + action.num } default: return state } } module.exports = reducer
const { createStore } = require('redux') const reducer = require('./reducer') // 創(chuàng)建store const store = createStore(reducer) module.exports = store
const store = require('./store') const { changeNameAction, addNumberAction } = require('./store/actionCreators') store.dispatch(changeNameAction('獨(dú)孤月')) store.dispatch(addNumberAction(100)) console.log(store.getState()) // { name: '獨(dú)孤月', counter: 200 }
- constants.js
- reducer.js
- index.js
- util.js 中使用
四、Redux 在 React中使用
4.1 先來(lái)一個(gè)案例
有兩個(gè)組件,組件上展示同一個(gè)counter,并且兩者能夠?qū)ounter進(jìn)行操作
- 創(chuàng)建redux對(duì)應(yīng)的store文件夾
actionCreators.js
import * as actionTypes from './constants' export const addNumberAction = (num) => ({ type: actionTypes.ADD_NUMBER, num }) export const subNumberAction = (num) => ({ type: actionTypes.SUB_NUMBER, num })
constants.js
export const ADD_NUMBER = "add_number" export const SUB_NUMBER = "sub_number"
reducer.js
import * as actionTypes from './constants' const initialState = { counter: 100 } 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 } } export default reducer
index.js
import { createStore } from "redux" import reducer from "./reducer" const store = createStore(reducer) export default store
組件中使用:
import React, { PureComponent } from 'react' // 引入store import store from '../store' import { addNumberAction } from '../store/actionCreators' export default 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> ) } }
import React, { PureComponent } from 'react' // 引入store import store from '../store' import { subNumberAction } from '../store/actionCreators' export default class Profile extends PureComponent { constructor() { super() this.state = { counter: store.getState().counter } } componentDidMount() { store.subscribe(() => { const state = store.getState() this.setState({ counter: state.counter }) }) } subNumber(num) { store.dispatch(subNumberAction(num)) } render() { const { counter } = this.state return ( <div> <h2>Profile Counter: {counter}</h2> <div> <button onClick={e => this.subNumber(1)}>-1</button> <button onClick={e => this.subNumber(5)}>-5</button> <button onClick={e => this.subNumber(8)}>-8</button> </div> </div> ) } }
- componentDidMount生命周期
- store.subscribe(() => {}) => this.state => render
- 修改數(shù)據(jù):store.dispatch(addNumberAction(num))
- Home.jsx
- Profile.jsx
4.2 react-redux使用
安裝:npm install react-redux
在使用時(shí)在入口文件中導(dǎo)入 Provider
,傳入 store
import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; 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> );
在 About 組件中使用:
import React, { PureComponent } from 'react' import { connect } from 'react-redux' import { addNumberAction, subNumberAction } from '../store/actionCreators' export class About extends PureComponent { clacNumber(num, isAdd) { if(isAdd) { this.props.addNumber(num) } else { this.props.subNumber(num) } } render() { const { counter } = this.props return ( <div> <h2>About Counter: {counter}</h2> <button onClick={e => this.clacNumber(6, true)}>+6</button> <button onClick={e => this.clacNumber(9, true)}>+9</button> <button onClick={e => this.clacNumber(6, false)}>-6</button> <button onClick={e => this.clacNumber(9, false)}>-9</button> </div> ) } } // connect() 返回值是一個(gè)高階組件 // function mapStateToProps(state) { // return { // counter: state.counter // } // } const mapStateToProps = (state) => ({ counter: state.counter }) const mapDispatchToProps = (dispatch) => ({ addNumber: num => dispatch(addNumberAction(num)), subNumber: num => dispatch(subNumberAction(num)) }) export default connect(mapStateToProps, mapDispatchToProps)(About)
connect():
- 傳入的第一個(gè)函數(shù)是映射當(dāng)前組件所需要的數(shù)據(jù)(store中可能有很多數(shù)據(jù),比如books、counter,而此處只需要counter)
- 傳入的第二個(gè)函數(shù)是映射
dispatch
到 props - 返回一個(gè)高階組件
4.3 組件中的異步操作
4.3.1 類組件生命周期中請(qǐng)求數(shù)據(jù)
- 在class組件的componentDidMount中發(fā)送請(qǐng)求
通過(guò)發(fā)起action
將請(qǐng)求的數(shù)據(jù)保存到store中
action方法:
export const changeBannersAction = (banners) => ({ type: actionTypes.CHANGE_BANNERS, banners }) export const changeRecommendsAction = (recommends) => ({ type: actionTypes.CHANGE_RECOMMENDS, recommends })
import * as actionTypes from './constants' const initialState = { counter: 100, banners: [], recommends: [] } function reducer(state = initialState, action) { switch (action.type) { case actionTypes.CHANGE_BANNERS: return { ...state, banners: action.banners } case actionTypes.CHANGE_RECOMMENDS: return { ...state, recommends: action.recommends } default: return state } } export default reducer
import React, { PureComponent } from 'react' import { connect } from 'react-redux' import axios from 'axios' import { changeBannersAction, changeRecommendsAction } from '../store/actionCreators' export class Category extends PureComponent { componentDidMount() { // 發(fā)送請(qǐng)求 axios.get('http://123.207.32.32:8000/home/multidata').then(res => { const banners = res.data.data.banner.list const recommends = res.data.data.recommend.list this.props.changeBanners(banners) this.props.changeRecommends(recommends) }) } render() { return ( <div> <h2>Category Page</h2> </div> ) } } const mapDispatchToProps = (dispacth) => ({ changeBanners: banners => dispacth(changeBannersAction(banners)), changeRecommends: recommends => dispacth(changeRecommendsAction(recommends)) }) export default connect(null, mapDispatchToProps)(Category)
- reducer
- category組件
4.3.2 使用中間件
上面的代碼有一個(gè)缺陷:
- 我們必須將網(wǎng)絡(luò)請(qǐng)求的異步代碼放到組件的生命周期中來(lái)完成
- 事實(shí)上,網(wǎng)絡(luò)請(qǐng)求到的數(shù)據(jù)也屬于狀態(tài)管理的一部分,更好的一種方式應(yīng)該是將其也交給redux來(lái)管理
如何將異步請(qǐng)求交給 Redux?
- 一個(gè)普通的action,返回的是一個(gè)對(duì)象
{ type: CHANGE_COUNTER, num: 10 }
- 對(duì)象中是無(wú)法直接拿到服務(wù)器請(qǐng)求到的異步數(shù)據(jù)的,但是如果返回一個(gè)函數(shù)呢?
- 返回一個(gè)函數(shù),然后在組件中發(fā)起 Action 的時(shí)候,執(zhí)行這個(gè)函數(shù)是不是就能夠拿到數(shù)據(jù)了呢?。?!
- !?。?/strong> 普通的 action 不能返回函數(shù),可以借助中間件來(lái)增強(qiáng)一下,讓他支持返回一個(gè)函數(shù),官網(wǎng)推薦的中間件:redux-thunk
- 中間件的目的:是在dispatch的action和最終達(dá)到的reducer之間,擴(kuò)展一些自己的代碼
redux-thunk 做了什么呢
- 讓
dispatch(action函數(shù))
中的action可以是一個(gè)函數(shù); - 該函數(shù)會(huì)被調(diào)用,并且會(huì)傳給這個(gè)函數(shù)一個(gè)dispatch函數(shù)和getState函數(shù);
- dispatch函數(shù)用于之后再次派發(fā)action
- getState函數(shù)考慮到我們之后的一些操作需要依賴原來(lái)的狀態(tài),用于獲取之前的一些狀態(tài)
代碼演示:
import { createStore, applyMiddleware } from "redux" import thunk from "redux-thunk" import reducer from "./reducer" // 正常情況下 store.dispatch(object) // 想要派發(fā)函數(shù) store.dispatch(function) // applyMiddleware 可以傳入多個(gè)中間件,","隔開 const store = createStore(reducer, applyMiddleware(thunk)) export default store
import * as actionTypes from './constants' import axios from 'axios' export const changeBannersAction = (banners) => ({ type: actionTypes.CHANGE_BANNERS, banners }) export const changeRecommendsAction = (recommends) => ({ type: actionTypes.CHANGE_RECOMMENDS, recommends }) export const fetchHomeMultidataAction = () => { // 如果是一個(gè)普通的action,需要返回action對(duì)象 // 問(wèn)題: 對(duì)象中不能直接拿到從服務(wù)器請(qǐng)求的異步數(shù)據(jù) // redux 不允許返回一個(gè)函數(shù),需要中間件 return (dispatch, getState) => { // console.log(getState().counter) // 100 // 進(jìn)行異步操作: 網(wǎng)絡(luò)請(qǐng)求 axios.get('http://123.207.32.32:8000/home/multidata').then(res => { const banners = res.data.data.banner.list const recommends = res.data.data.recommend.list // dispatch({type: actionTypes.CHANGE_BANNERS, banners}) // dispatch({type: actionTypes.CHANGE_RECOMMENDS, recommends}) dispatch(changeBannersAction(banners)) dispatch(changeRecommendsAction(recommends)) }) } }
import React, { PureComponent } from 'react' import { connect } from 'react-redux' import { fetchHomeMultidataAction } from '../store/actionCreators' export class Category extends PureComponent { componentDidMount() { this.props.fetchHomeMultidata() } render() { return ( <div> <h2>Category Page</h2> </div> ) } } const mapStateToProps = state => ({ counter: state.counter }) const mapDispatchToProps = (dispacth) => ({ fetchHomeMultidata: () => dispacth(fetchHomeMultidataAction()) }) export default connect(mapStateToProps, mapDispatchToProps)(Category)
- store(index.js) 中引入thunk
- actionCreators.js
- 組件中使用
4.4 redux-devtools
redux可以方便的對(duì)狀態(tài)進(jìn)行跟蹤和調(diào)試
- redux官網(wǎng)提供了redux-devtools的工具
- 利用這個(gè)工具,可以知道每次狀態(tài)是如何被修改的,修改前后的狀態(tài)變化等等
安裝該工具需要兩步:
- 在對(duì)應(yīng)的瀏覽器中安裝相關(guān)的插件(比如Chrome瀏覽器擴(kuò)展商店中搜索Redux DevTools即可)
- 在redux中繼承devtools的中間件
默認(rèn)該工具是未開啟的,開發(fā)環(huán)境開啟需要進(jìn)行配置,生產(chǎn)環(huán)境千萬(wàn)千萬(wàn)不要打開哦!??!
import { createStore, applyMiddleware, compose } from "redux" import thunk from "redux-thunk" import reducer from "./reducer" // redux-devtools const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({trace: true}) || compose; const store = createStore(reducer, composeEnhancers(applyMiddleware(thunk))) export default store 復(fù)制代碼
4.5 模塊拆分
正常情況下,我們的 store 中應(yīng)該是有不同狀態(tài)的數(shù)據(jù),比如:購(gòu)物車、用戶信息等等, 如果將所有的狀態(tài)都放到一個(gè)reducer中進(jìn)行管理,隨著項(xiàng)目的日趨龐大,必然會(huì)造成代碼臃腫、難以維護(hù)。因此,我們可以對(duì)reducer進(jìn)行拆分。
以上面提到的案例為例,抽取一個(gè) counter 的reducer和一個(gè) home 的reducer,再將其合并起來(lái)
分不同的模塊,每個(gè)模塊都包含自己的核心:
>reducer:接收action對(duì)象,返回最新的state
- constants:定義常量數(shù)據(jù)
- actioncreators:定義創(chuàng)建action對(duì)象的函數(shù)
- index:導(dǎo)出reducer
在 index.js
中導(dǎo)入每一個(gè)模塊的內(nèi)容,通過(guò)combineReducers
合并之后放入createStore
import { createStore, applyMiddleware, compose, combineReducers } from "redux" import thunk from "redux-thunk" import counterReducer from './counter' import homeReducer from './home' import userReducer from './user' // 將reducer合并到一起 const reducer = combineReducers({ counter: counterReducer, home: homeReducer, user: userReducer }) // redux-devtools const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({trace: true}) || compose; const store = createStore(reducer, composeEnhancers(applyMiddleware(thunk))) export default store
combineReducers 如何實(shí)現(xiàn)合并呢?
- 事實(shí)上,它是將我們傳入的reducers合并到一個(gè)對(duì)象中,最終返回一個(gè)combination的函數(shù)(相當(dāng)于我們的reducer函數(shù)了)
- 在執(zhí)行combination函數(shù)的過(guò)程中,它會(huì)通過(guò)判斷前后返回的數(shù)據(jù)是否相同來(lái)決定返回之前的state還是新的state
- 新的state會(huì)觸發(fā)訂閱者發(fā)生對(duì)應(yīng)的刷新,而舊的state可以有效的阻止訂閱者發(fā)生刷新
// combineReducers 原理 function reducer(state = {}, action) { // 返回一個(gè)對(duì)象,store中的state return { counter: counterReducer(state.counter, action), home: homeReducer(state.home, action), user: userReducer(state.user, action) } }
到此這篇關(guān)于一文詳解React Redux使用方法的文章就介紹到這了,更多相關(guān)React Redux使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于React實(shí)現(xiàn)無(wú)限滾動(dòng)表格
以文本為例,為了實(shí)現(xiàn)無(wú)限循環(huán)的視覺(jué)效果,我們需要準(zhǔn)備兩段相同的文本,并讓第二段文本的頭部銜接在第一段文本的尾部,同時(shí),為兩段文本設(shè)置相同的滾動(dòng)動(dòng)畫,本文給大家介紹了基于React實(shí)現(xiàn)無(wú)限滾動(dòng)表格,需要的朋友可以參考下2023-11-11使用React+SpringBoot開發(fā)一個(gè)協(xié)同編輯的表格文檔實(shí)現(xiàn)步驟
隨著云計(jì)算和團(tuán)隊(duì)協(xié)作的興起,協(xié)同編輯成為了許多企業(yè)和組織中必不可少的需求,本文小編就將為大家介紹如何使用React+SpringBoot簡(jiǎn)單的開發(fā)一個(gè)協(xié)同編輯的表格文檔,感興趣的朋友一起看看吧2023-11-11React Native:react-native-code-push報(bào)錯(cuò)的解決
這篇文章主要介紹了React Native:react-native-code-push報(bào)錯(cuò)的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10再次談?wù)揜eact.js實(shí)現(xiàn)原生js拖拽效果引起的一系列問(wèn)題
React 起源于 Facebook 的內(nèi)部項(xiàng)目,因?yàn)樵摴緦?duì)市場(chǎng)上所有 JavaScript MVC 框架,都不滿意,就決定自己寫一套,用來(lái)架設(shè) Instagram 的網(wǎng)站.本文給大家介紹React.js實(shí)現(xiàn)原生js拖拽效果,需要的朋友一起學(xué)習(xí)吧2016-04-04Hello?React的組件化方式之React入門小案例演示
這篇文章主要介紹了Hello?React的組件化方式-React入門小案例,本文通過(guò)Hello?React的案例,?來(lái)體驗(yàn)一下React開發(fā)模式,?以及jsx的語(yǔ)法,需要的朋友可以參考下2022-10-10react render的原理及觸發(fā)時(shí)機(jī)說(shuō)明
這篇文章主要介紹了react render的原理及觸發(fā)時(shí)機(jī)說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02