日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
React 狀態(tài)管理 - 你可能不需要 Redux,但你需要了解它!

?:既然不需要,為什么需要了解?

創(chuàng)新互聯(lián)公司是專業(yè)的思茅網(wǎng)站建設(shè)公司,思茅接單;提供成都網(wǎng)站建設(shè)、網(wǎng)站建設(shè),網(wǎng)頁(yè)設(shè)計(jì),網(wǎng)站設(shè)計(jì),建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行思茅網(wǎng)站開發(fā)網(wǎng)頁(yè)制作和功能擴(kuò)展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團(tuán)隊(duì),希望更多企業(yè)前來合作!

?:不了解你怎么知道不需要呢?

?:這話沒毛病,學(xué)它!

Redux 是一個(gè)可預(yù)測(cè)的狀態(tài)管理容器,也是 react 中最流行的一個(gè)狀態(tài)管理工具,無論是工作或面試只要你使用了 react 都需要掌握它。核心理念是在全局維護(hù)了一個(gè)狀態(tài),稱為 store,為應(yīng)用系統(tǒng)提供了全局狀態(tài)管理的能力,使得跨組件通信變得更簡(jiǎn)單。

Redux 抽象程度很高,關(guān)注的是 “哲學(xué)設(shè)計(jì)”,開發(fā)者最關(guān)心的是 “如何實(shí)現(xiàn)”,做為初學(xué)者盡管看了官網(wǎng) API 介紹但面對(duì)實(shí)際項(xiàng)目時(shí)還是發(fā)現(xiàn)無從入手,特別是面對(duì)一些新名詞 store、state、action、dispatch、reducer、middlwware 時(shí),有些小伙伴表示我就認(rèn)識(shí) state...

本篇在介紹一些 Redux 的概念后會(huì)重構(gòu)上一節(jié) useReducer + useContext 實(shí)現(xiàn)的 Todos,介紹如何在 React 中應(yīng)用 Redux,從實(shí)踐中學(xué)習(xí)。

Redux 數(shù)據(jù)流轉(zhuǎn)過程

Redux 通過一系列規(guī)范約定來約束應(yīng)用程序如何根據(jù) action 來更新 store 中的狀態(tài),下圖展示了 Redux 數(shù)據(jù)流轉(zhuǎn)過程,也是 Redux 的主要組成部分:

  • View:Redux 不能單獨(dú)工作,需要結(jié)合 React/Vue/Angular 等 View 層框架工作,通常 Redux 主要應(yīng)用于 React 框架中,渲染時(shí)頁(yè)面從 Redux store 中獲取數(shù)據(jù)渲染展現(xiàn)給用戶。
  • Action:當(dāng)頁(yè)面想改變 store 里的數(shù)據(jù),通過 dispatch 方法派發(fā)一個(gè) action 給 store(例如,請(qǐng)求接口響應(yīng)之后派發(fā) action 改變數(shù)據(jù)狀態(tài)),這里的 action 是 store 唯一的信息來源,做為一個(gè)信息的載體存在。
  • Store:store 是鏈接 action 和 reducer 的橋梁,它在收到 action 后會(huì)把之前的 state 和 action 一起發(fā)給 reducer。
  • Reducer:reducer 主要責(zé)任是計(jì)算下一個(gè)狀態(tài),因此它在接收到之前的 state 和 action 之后會(huì)返回新的數(shù)據(jù)給到 store(這里要保證 reducer 是一個(gè)純函數(shù)),最終 store 更新自己數(shù)據(jù)告訴頁(yè)面,回到 View 層頁(yè)面自動(dòng)刷新。

圖片來源:redux application data flow

Immutable

在 reducer 純函數(shù)中不允許直接修改 state 對(duì)象,每次都應(yīng)返回一個(gè)新的 state。原生 JavaScript 中我們要時(shí)刻記得使用 ES6 的擴(kuò)展符 ... 或 Object.assign() 函數(shù)創(chuàng)建一個(gè)新 state,但是仍然是一個(gè)淺 copy,遇到復(fù)雜的數(shù)據(jù)結(jié)構(gòu)我們還需要做深拷貝返回一個(gè)新的狀態(tài),總之你要保證每次都返回一個(gè)新對(duì)象,一方面深拷貝會(huì)造成性能損耗、另一方面難免會(huì)忘記從而直接修改原來的 state。

Immutable 數(shù)據(jù)一旦創(chuàng)建,對(duì)該數(shù)據(jù)的增、刪、改操作都會(huì)返回一個(gè)新的 immutable 對(duì)象,保證了舊數(shù)據(jù)可用同時(shí)不可變。

Immutable 實(shí)現(xiàn)的原理是 Persistent Data Structure(持久化數(shù)據(jù)結(jié)構(gòu)),也就是使用舊數(shù)據(jù)創(chuàng)建新數(shù)據(jù)時(shí),要保證舊數(shù)據(jù)同時(shí)可用且不變。同時(shí)為了避免 deepCopy 把所有節(jié)點(diǎn)都復(fù)制一遍帶來的性能損耗,Immutable 使用了 Structural Sharing(結(jié)構(gòu)共享),即如果對(duì)象樹中一個(gè)節(jié)點(diǎn)發(fā)生變化,只修改這個(gè)節(jié)點(diǎn)和受它影響的父節(jié)點(diǎn),其它節(jié)點(diǎn)則進(jìn)行共享。參考 Immutable 詳解及 React 中實(shí)踐

請(qǐng)看下面動(dòng)畫:

在本文中整個(gè) redux store 狀態(tài)樹都采用的是 Immutable 數(shù)據(jù)對(duì)象,同時(shí)使用時(shí)也應(yīng)避免與普通的 JavaScript 對(duì)象混合使用,從下面例子中可以學(xué)習(xí)到一些常用的 API 使用,更詳細(xì)的介紹參考官網(wǎng)文檔 immutable-js.com/docs。

項(xiàng)目結(jié)構(gòu)

React + Redux 項(xiàng)目的組織結(jié)構(gòu),在第一次寫項(xiàng)目時(shí)也犯了困惑,你如果在社區(qū)中搜索會(huì)發(fā)現(xiàn)很多種聲音,例如,按類型劃分(類似于 MVC 這樣按不同的角色劃分)、頁(yè)面功能劃分、Ducks(將 actionTypes、actionCreators、reducer 放在一個(gè)文件里),這里每一種的區(qū)別也可以單獨(dú)寫篇文章討論了,本節(jié)采用的方式是按頁(yè)面功能劃分,也是筆者剛開始寫 React + Redux 時(shí)的一種目錄組織方式。沒有最佳的方式,選擇適合于你的方式。

按照一個(gè)頁(yè)面功能對(duì)應(yīng)一個(gè)文件夾劃分,pages/todos 文件夾負(fù)責(zé)待辦事項(xiàng)功能,如果頁(yè)面復(fù)雜可在頁(yè)面組件內(nèi)創(chuàng)建一個(gè) pages/todos/components 文件夾,redux 相關(guān)的 action、reducer 等放在 page/todos/store 文件夾中。

src/
├── App.css
├── App.js
├── index.css
├── index.js
├── components
├── pages
│ └── todos
│ ├── components
│ │ ├── Todo.jsx
│ │ └── TodoAdd.jsx
│ ├── index.jsx
│ └── store
│ ├── actionCreators.js
│ ├── constants.js
│ ├── index.js
│ └── reducer.js
├── reducers
│ └── todos-reducer.js
├── routes
│ └── index.js
└── store
├── index.js
└── reducer.js

Todos View 層展示組件

View 層我們定義為 “展示組件”,負(fù)責(zé) UI 渲染,至于渲染時(shí)用到的數(shù)據(jù)如何獲取交由后面的容器組件負(fù)責(zé)。以下 Todo、TodoAdd、Todos 三個(gè)組件中使用到的數(shù)據(jù)都是從 props 屬性獲取,后面容器組件鏈接 React 與 Redux 時(shí)會(huì)再講。

Todo 組件

組件位置src/pages/todos/components/Todo.jsx

import { useState } from "react";

/**
* Todo component
* @param {Number} props.todo.id
* @param {String} props.todo.content
* @param {Function} props.editTodo
* @param {Function} props.removeTodo
* @returns
*/
const Todo = ({ todo, editTodo, removeTodo }) => {
console.log('Todo render');
const [isEdit, setIsEdit] = useState(false);
const [content, setContent] = useState(todo.get('content'));

return

{
!isEdit ? <>
{todo.get('content')}



: <>

setContent(e.target.value) } />




}

}

export default Todo;

Todos 組件

組件位置src/pages/todos/index.jsx。

import { useState } from "react";

import { actionCreators } from '../store';

/**
* Add todo component
* @param {Function} props.addTodo
* @returns
*/
const TodoAdd = ({ addTodo }) => {
console.log('TodoAdd render');
const [content, setContent] = useState('');

return

setContent(e.target.value)} />


};

export default TodoAdd;

唯一數(shù)據(jù)源Store

一個(gè) React + Redux 的應(yīng)用程序中只有一個(gè) store,是應(yīng)用程序的唯一數(shù)據(jù)源,類似于在我們應(yīng)用中抽象出一個(gè)狀態(tài)樹,與組件一一關(guān)聯(lián)。這也是一種集中式管理應(yīng)用狀態(tài)的方式,也是和 React hooks 提供的 useState/useReducer 一個(gè)重大區(qū)別之處。

創(chuàng)建 store

通過 redux 的 createStore() 方法創(chuàng)建 store,支持預(yù)設(shè)一些初始化狀態(tài)。

代碼位置src/store/index.js。

import { createStore, compose } from 'redux';
import reducer from './reducer';

const store = createStore(reducer, /* preloadedState, */);

export default store;

reducer 拆分與組裝

當(dāng)應(yīng)用復(fù)雜時(shí)我們通常會(huì)拆分出多個(gè)子 reducer 函數(shù),每個(gè) reducer 處理自己負(fù)責(zé)的 state 數(shù)據(jù)。例如,按頁(yè)面功能劃分項(xiàng)目結(jié)構(gòu),每個(gè)頁(yè)面/公共組件都可以維護(hù)自己的 reducer。

有了拆分,對(duì)應(yīng)還有組合,redux 為我們提供了 combineReducers 函數(shù)用于合并多個(gè) reducer。因?yàn)槲覀兊?state 是一個(gè) Immutable 對(duì)象,而 redux 提供的 combineReducers 只支持原生 JavaScript 對(duì)象,不能操作 Immutable 對(duì)象,我們還需要借助另外一個(gè)中間件 **redux-immutable** 從 state 取出 Immutable 對(duì)象。

可以為 reducer 函數(shù)指定不同的 key 值,這個(gè) key 值在組件從 store 獲取 state 時(shí)會(huì)用到,下文 “容器組件鏈接 React 與 Redux” 中會(huì)使用到。

代碼位置src/store/reducer.js。

import { combineReducers } from 'redux-immutable';
import { reducer as todosReducer } from '../pages/todos/store';
import { reducer as otherComponentReducer } from '../pages/other-component/store';

const reducer = combineReducers({
todosPage: todosReducer,
otherComonpent: otherComponentReducer, // 其它組件的 reducer 函數(shù),在這里依次寫
});

export default reducer;

為 todos 組件創(chuàng)建 store 文件

代碼位置:src/pages/todos/store/index.js。

import * as constants from './constants';
import * as actionCreators from './actionCreators';
import reducer from './reducer';

export {
reducer,
constants,
actionCreators,
};

constants

代碼位置:src/pages/todos/store/constants.js。

export const TODO_LIST = 'todos/TODO_LIST';
export const TODO_LIST_ADD = 'todos/TODO_LIST_ADD';
export const TODO_LIST_EDIT = 'todos/TODO_LIST_EDIT';
export const TODO_LIST_REMOVE = 'todos/TODO_LIST_REMOVE';

創(chuàng)建 action creator 與引入中間件

action 是 store 唯一的信息來源,action 的數(shù)據(jù)結(jié)構(gòu)要能清晰描述實(shí)際業(yè)務(wù)場(chǎng)景,通常 type 屬性是必須的,描述類型。我的習(xí)慣是放一個(gè) payload 對(duì)象,描述類型對(duì)應(yīng)的數(shù)據(jù)內(nèi)容。

一般會(huì)通過 action creator 創(chuàng)建一個(gè) action。例如,以下為一個(gè)獲取待辦事項(xiàng)列表的 action creator,這種寫法是同步的。

function getTodos() {
return {
type: 'TODO_LIST',
payload: {}
}
}

在實(shí)際的業(yè)務(wù)中,異步操作是必不可少的,而 store.dispatch 方法只能處理普通的 JavaScript 對(duì)象,如果返回一個(gè)異步 function 代碼就會(huì)報(bào)錯(cuò)。通常需要結(jié)合 redux-thunk 中間件使用,實(shí)現(xiàn)思路是** action creator 返回的異步函數(shù)先經(jīng)過 redux-thunk 處理,當(dāng)真正的請(qǐng)求響應(yīng)后,在發(fā)送一個(gè) dispatch(action) 此時(shí)的 action 就是一個(gè)普通的 JavaScript 對(duì)象了**。

Redux 的中間件概念與 Node.js 的 Web 框架 Express 類似,通用的邏輯可以抽象出來做為一個(gè)中間件,一個(gè)請(qǐng)求先經(jīng)過中間件處理后 -> 到達(dá)業(yè)務(wù)處理邏輯 -> 業(yè)務(wù)邏輯響應(yīng)之后 -> 響應(yīng)再到中間件。redux 里的 action 好比 Web 框架收到的請(qǐng)求。

代碼位置:src/store/index.js。修改 store 文件,引入中間件使得 action 支持異步操作。

import { createStore, compose, applyMiddleware } from 'redux'; // 導(dǎo)入 compose、applyMiddleware
import chunk from 'redux-thunk'; // 導(dǎo)入 redux-thunk 包
import reducer from './reducer';

const store = createStore(reducer, /* preloadedState, */ compose(
applyMiddleware(chunk),
));

export default store;

創(chuàng)建本次 todos 需要的 action creator,實(shí)際業(yè)務(wù)中增、刪、改、查我們會(huì)調(diào)用服務(wù)端的接口查詢或修改數(shù)據(jù),為了模擬異步,我們簡(jiǎn)單點(diǎn)使用 Promise 模擬異步操作。

代碼位置:src/pages/todos/store/actionCreators.js。


import { TODO_LIST, TODO_LIST_ADD, TODO_LIST_REMOVE, TODO_LIST_EDIT } from './constants';

const randomID = () => Math.floor(Math.random() * 10000);

// 獲取待辦事項(xiàng)列表
export const getTodos = () => async dispatch => {
// 模擬 API 異步獲取數(shù)據(jù)
const todos = await Promise.resolve([
{
id: randomID(),
content: '學(xué)習(xí) React',
},
{
id: randomID(),
content: '學(xué)習(xí) Node.js',
}
]);

const action = {
type: TODO_LIST,
payload: {
todos
}
};
dispatch(action);
}

// 添加待辦事項(xiàng)
export const addTodo = (content) => async dispatch => {
const result = await Promise.resolve({
id: randomID(),
content,
});

const action = {
type: TODO_LIST_ADD,
payload: result
};
dispatch(action);
}

// 編輯待辦事項(xiàng)
export const editTodo = (id, content) => async dispatch => {
const result = await Promise.resolve({ id, content });
const action = {
type: TODO_LIST_EDIT,
payload: result,
};
dispatch(action);
}

// 移除待辦事項(xiàng)
export const removeTodo = id => async dispatch => {
const result = await Promise.resolve({ id });
const action = {
type: TODO_LIST_REMOVE,
payload: result,
};
dispatch(action);
}

reducer 純函數(shù)

reducer 根據(jù) action 的響應(yīng)決定怎么去修改 store 中的 state。編寫 reducer 函數(shù)沒那么復(fù)雜,倒要切記該函數(shù)始終為一個(gè)純函數(shù),應(yīng)避免直接修改 state。reducer 純函數(shù)要保證以下兩點(diǎn):

  • 同樣的參數(shù),函數(shù)的返回結(jié)果也總是相同的。例如,根據(jù)上一個(gè) state 和 action 也會(huì)返回一個(gè)新的 state,類似這樣的結(jié)構(gòu) (previousState, action) => newState。
  • 函數(shù)執(zhí)行沒有任何副作用,不受外部執(zhí)行環(huán)境的影響。例如,不會(huì)有任何的接口調(diào)用或修改外部對(duì)象。

需要注意一點(diǎn)是在第一次調(diào)用時(shí) state 為 undefined,這時(shí)需使用 initialState 初始化 state。

代碼位置:src/pages/todos/store/reducer.js。

import { fromJS } from 'immutable';
import { TODO_LIST, TODO_LIST_ADD, TODO_LIST_REMOVE, TODO_LIST_EDIT } from './constants';

export const initialState = fromJS({
todos: [],
});

const reducer = (state = initialState, action = {}) => {
switch (action.type) {
case TODO_LIST: {
return state.merge({
todos: state.get('todos').concat(fromJS(action.payload.todos)),
});
}
case TODO_LIST_ADD: {
return state.set('todos', state.get('todos').push(fromJS({
id: action.payload.id,
content: action.payload.content,
})));
}
case TODO_LIST_EDIT: {
return state.merge({
todos: state.get('todos').map(item => {
if (item.get('id') === action.payload.id) {
const newItem = { ...item.toJS(), content: action.payload.content };
return fromJS(newItem);
}
return item;
})
})
}
case TODO_LIST_REMOVE: {
return state.merge({
todos: state.get('todos').filter(item => item.get('id') !== action.payload.id),
})
}
default: return state;
}
};

export default reducer;

容器組件鏈接 React 與 Redux

Redux 做為一個(gè)狀態(tài)管理容器,本身并沒有與任何 View 層框架綁定,當(dāng)在 React 框架中使用 Redux 時(shí)需安裝 react-redux npm i react-redux -S 庫(kù)。

容器組件

react-redux 提供的 connect 函數(shù),可以把 React 組件和 Redux 的 store 鏈接起來生成一個(gè)新的容器組件(這里有個(gè)經(jīng)典的設(shè)計(jì)模式 “高階組件”),數(shù)據(jù)如何獲取就是容器組件需要負(fù)責(zé)的事情,在獲取到數(shù)據(jù)后通過 props 屬性傳遞到展示組件,當(dāng)展示組件需要變更狀態(tài)時(shí)調(diào)用容器組件提供的方法同步這些狀態(tài)變化。

總結(jié)下來,容器組件需要做兩件事:

  • 從 Redux 的 store 中獲取數(shù)據(jù)給到展示組件,對(duì)應(yīng)下例 mapStateToProps() 方法。
  • 提供方法供展示組件同步需要變更的狀態(tài),對(duì)應(yīng)下例 mapDispatchToProps() 方法。
// 創(chuàng)建容器組件代碼示例
import { connect } from 'react-redux';
import ExampleComponent from './ExampleComponent'

const mapStateToProps = (state) => ({ // 從全局狀態(tài)取出數(shù)據(jù)映射到展示組件的 props
todos: state.getIn(['todosComponent', 'todos']),
});

const mapDispatchToProps = (dispatch) => ({ // 把展示組件變更狀態(tài)需要用到的方法映射到展示組件的 props 上。
getTodos() {
dispatch(actionCreators.getTodos());
},
});

export default connect(mapStateToProps, mapDispatchToProps)(ExampleComponent);

上例,當(dāng) redux store 中的 state 變化時(shí),對(duì)應(yīng)的 mapStateToProps 函數(shù)會(huì)被執(zhí)行,如果 mapStateToProps 函數(shù)新返回的對(duì)象與之前對(duì)象淺比較相等(此時(shí),如果是類組件可以理解為 shouldComponentUpdate 方法返回 false),展示組件就不會(huì)重新渲染,否則重新渲染展示組件。

展示組件與容器組件之間的關(guān)系可以自由組合,可以單獨(dú)創(chuàng)建一個(gè) container 文件,來包含多個(gè)展示組件,同樣也可以在展示組件里包含容器組件。在我們的示例中,也比較簡(jiǎn)單是在展示組件里返回一個(gè)容器組件,下面開始修改我們展示組件。

修改 Todo 組件

組件位置src/pages/todos/components/Todo.jsx。

在我們的 Todo 組件中,參數(shù) todo 是由上層的 Todos 組件傳遞的這里并不需要從 Redux 的 store 中獲取 state,只需要修改狀態(tài)的函數(shù)就可以了,connect() 函數(shù)第一個(gè)參數(shù) state 可以省略,這樣 state 的更新也就不會(huì)引起該組件的重新渲染了。

import { connect } from 'react-redux';

const Todo = ({ todo, editTodo, removeTodo }) => {...} // 中間代碼省略

const mapDispatchToProps = (dispatch) => ({
editTodo(id, content) {
dispatch(actionCreators.editTodo(id, content));
},
removeTodo(id) {
dispatch(actionCreators.removeTodo(id));
}
});

export default connect(null, mapDispatchToProps)(Todo);

修改 TodoAdd 組件

組件位置src/pages/todos/components/TodoAdd.jsx。

import { connect } from 'react-redux';

const TodoAdd = ({ addTodo }) => {...}; // 中間代碼省略

const mapDispatchToProps = (dispatch) => ({
addTodo(content) {
dispatch(actionCreators.addTodo(content));
},
});

export default connect(null, mapDispatchToProps)(TodoAdd);

修改 Todos 組件

組件位置src/pages/todos/components/Todos.jsx。

import { connect } from 'react-redux';

const Todos = ({ todos, getTodos }) => { ... } // 中間代碼省略

const mapStateToProps = (state) => ({
todos: state.getIn(['todosPage', 'todos']),
});

const mapDispatchToProps = (dispatch) => ({
getTodos() {
dispatch(actionCreators.getTodos());
},
});

export default connect(mapStateToProps, mapDispatchToProps)(Todos);

創(chuàng)建 MyRoutes 組件

有了 Page 相應(yīng)的也有路由,創(chuàng)建 MyRoutes 組件,代碼位置 src/routes/index.js。

import {
BrowserRouter, Routes, Route
} from 'react-router-dom';
import Todos from '../pages/todos';

const MyRoutes = () => {
return (


} />


);
};

export default MyRoutes;

Provider 組件傳遞 store

通過 react-redux 的 connect 函數(shù)創(chuàng)建的容器組件可以獲取 redux store,那么有沒有想過容器組件又是如何獲取的 redux store?

在 React 狀態(tài)管理 - Context 一篇中介紹過,使用 React.createContext() 方法創(chuàng)建一個(gè)上下文(MyContext),之后通過 MyContext 提供的 Provider 組件可以傳遞 value 屬性供子組件使用。react-redux 也提供了一個(gè) Provider 組件,正是通過 context 傳遞 store 供子組件使用,所以我們使用 redux 時(shí),一般會(huì)把 Provider 組件做為根組件,這樣被 Provider 根組件包裹的所有子組件都可以獲取到 store 中的存儲(chǔ)的狀態(tài)。

創(chuàng)建 App.js 組件,組件位置:src/app.js。

import { Provider } from 
                                                網(wǎng)站欄目:React 狀態(tài)管理 - 你可能不需要 Redux,但你需要了解它!                                                
本文網(wǎng)址:http://m.5511xx.com/article/coeocgi.html