Redux正在成為構(gòu)建React應(yīng)用程序公認(rèn)的方式。目前已經(jīng)有大量的示例來說明展示其用法蠕啄。但是React-Redux應(yīng)用程序由好幾個部分組成填具,如:Reducers
卡者、Actions
、Action Creators
挫掏、State
炉媒、Middleware
等,這些是難以理解的铺韧!
當(dāng)我開始學(xué)習(xí)它的時候,我沒有找到關(guān)于“React-Redux的哪個部分首先構(gòu)建缓淹?”的文章或者“如何最佳的構(gòu)建React-Redux應(yīng)用程序”哈打。所以我經(jīng)歷了幾個例子和博客工窍,并提出了如何處理大多數(shù)React Redux應(yīng)用程序的一般步驟。
為什么使用Redux前酿?
React - 一個幫助我們把應(yīng)用程序分解成多個組件的js庫,但是react沒有明確說明如何跟蹤數(shù)據(jù)(state)以及如何正確的處理事件(action)
Redux - 一個免費的react庫鹏溯,其提供了一種輕松保存數(shù)據(jù)(state)和事件(action)的方法罢维。
基本上Redux允許我們根據(jù)你的需要構(gòu)建React應(yīng)用程序,但將所有的state和action委托給Redux丙挽。
一個簡單的todo應(yīng)用程序有8個步驟肺孵,理論是,一些早期的框架在構(gòu)建todo應(yīng)用時非常簡單颜阐,但是構(gòu)建真實的應(yīng)用程序很難平窘,但是react redux恰恰相反。
讓我們開始:
第一步:撰寫一個詳細(xì)的應(yīng)用模擬
模擬需要包含所有的數(shù)據(jù)和視覺效果(如:列表項上面的刪除線或者過濾里的“All”使用文字替代link)
第二步:將應(yīng)用程序劃分為組件
嘗試根據(jù)每個組件的總體目的凳怨,將應(yīng)用程序分為多個組件瑰艘。
這里我們劃分為3個組件:AddTodo
、TodoList
肤舞、Filter
Redux術(shù)語:Actions
和 State
每一個組件都包含兩個事物:
- 根據(jù)一些數(shù)據(jù)渲染DOM紫新,這些數(shù)據(jù)成為
state
。 - 監(jiān)聽用戶或其他事件并發(fā)送給js函數(shù)李剖,這些被稱作為
action
芒率。
第三步:為每一個組件列出state和actions
請務(wù)必仔細(xì)查看第二步中的每一個組件,然后為它們列出state和actions列表篙顺。我們有3個組件AddTodo
偶芍、TodoList
和Filter
,讓我們來列出他們的action和state。
3.1 AddTodo組件:state和actions
在這個組件中德玫,我們沒有狀態(tài)匪蟀,因為組件外觀和感覺根據(jù)任何數(shù)據(jù)都不會改變,但是需要讓其他組件知道用戶何時創(chuàng)建一個新的todo宰僧,我們稱這個action為:ADD_TODO
萄窜。
3.2 TodoList 組件:state和actions
TodoList組件需要一個todo項的數(shù)組來渲染自己,所以它需要一個state:Todos
(Array類型),除此之外撒桨,它還需要知道當(dāng)前的Filter
條件查刻,來根據(jù)條件顯示或隱藏todoitems,所以它還需要另外一個state凤类,可以稱為VisibilityFilter
(boolen類型)穗泵。
再進(jìn)一步,TodoList允許我們切換某個todoitem是否已經(jīng)完成的狀態(tài)而且還需要讓其他組件知道這個切換谜疤,所以我們需要一個稱為TOGGLE_TODO
的action佃延。
3.3 Filter組件:state和actions
Filter
組件根據(jù)條件是否激活來渲染成link或者一個簡單的文字现诀,這里需要一個state:CurrentFilter
。
當(dāng)用戶點擊篩選條件時履肃,Filter
組件也需要告訴其它組件狀態(tài)的切換仔沿,所以需要一個action:SET_VIBILITY_FILTER
。
Redux術(shù)語:Action Creators
Action Creators都是簡單的函數(shù)尺棋,其主要職責(zé)是從DOM事件中接收數(shù)據(jù)封锉,將其格式化為正式的Action
json對象然后返回這個action對象。這有助于幫助我們規(guī)范化數(shù)據(jù)膘螟。
此外成福,它允許將來的任何其他組件也將這些動作發(fā)送給其他組件。
第四步:為每個action創(chuàng)建action creators
我們來為這三個(ADD_TODO
,TOGGLE_TODO
,SET_VISIBILITY_FILTER
)action來創(chuàng)建action creators荆残。
// 1. 從AddTodo字段獲取文本并返回適當(dāng)?shù)?Action'json對象奴艾,用以發(fā)送到其他組件
export const addTodo = (text) => {
return {
type: 'ADD_TODO',
id: nextTodoId++,
text, // ES6語法,相當(dāng)于ES5中的text: text,
completed: false // 默認(rèn)未完成
}
}
// 2. 獲取過濾字符串并返回適當(dāng)?shù)?Action'json對象内斯,用以發(fā)送到其他組件
export const setVisibilityFilter = (filter) => {
return {
type: 'SET_VISIBILITY_FILTER',
filter
}
}
// 3. 獲取todo項的id并返回適當(dāng)?shù)?Action'json對象蕴潦,用以發(fā)送到其他組件
export const toggleTodo = (id) => {
return {
type: 'TOGGLE_TODO',
id
}
}
Redux術(shù)語:Reducers
Reducers是一個從Redux中接受state和"action"json對象的函數(shù),其返回一個新的 state
存儲到Redux中俘闯。
- 當(dāng)有用戶操作時品擎,Reducer函數(shù)將會被'Container'調(diào)用。
- 當(dāng)reducer改變了state時备徐,Redux會把新的state傳遞給每一個組件萄传,react會重新渲染每一個組件。
舉一個例子蜜猾,下面的方法接受Redux的state(一個當(dāng)前todos的數(shù)組)秀菱,然后返回一個新的todos數(shù)組(new state),當(dāng)action的type為ADD_TODO
時蹭睡,一個新的todo將會被添加進(jìn)去衍菱。
const todo = (state = [], action) => {
switch (action.type) {
case 'ADD_TODO':
return [...state, {id: action.id, text: action.text, complete: false}];
}
}
第五步:為每個action編寫Reducers
注意:為了簡潔,以下代碼只是精簡版肩豁,只是簡單的展示了ADD_TODO
,TOGGLE_TODO
,SET_VISIBILITY_FILTER
脊串。
const todo = (state, action) => {
switch (action.type) {
case 'ADD_TODO':
return [...state, {id: action.id, text: action.text,
completed:false}];
case 'TOGGLE_TODO':
return state.map(todo => {
if (todo.id !== action.id) {
return todo
}
return Object.assign({}, todo, {completed: !todo.completed})
});
case 'SET_VISIBILITY_FILTER': {
return action.filter;
}
default:
return state;
}
}
Redux術(shù)語:Presentational
和Container
組件
在每個組件中編寫React和Redux的邏輯,可能會造成混亂清钥,所以Redux推薦創(chuàng)建一個名為presentational
的虛擬演示組件和一個名為Container
的父包裹組件琼锋,用于處理Redux,dispatch action
等祟昭。
Container組件將數(shù)據(jù)傳遞給presentational組件缕坎,處理事件,代表presentational組件處理react篡悟。
圖例:黃色虛線為presentational組件谜叹,黑色虛線為Container組件
第六步:實現(xiàn)每個presentational組件
6.1 實現(xiàn)AddTodoForm presentational組件
let AddTodoForm = ({onSubmit}) => {
let input;
return (
<div>
<form onSubmit={e => {onSubmit(input.value)}}>
<input ref={node => {input = node}} />
<button type="submit">Add todo</button>
</form>
</div>
)
}
6.2 實現(xiàn)TodoList presentational 組件
6.3 實現(xiàn)Link presentanional組件
注意:在實際的代碼中匾寝,Link presentational組件被包裝在FilterLink容器組件中。然后在Footer presentational 組件中顯示3個FilterLink組件荷腊。
第七步:為presentatinal組件創(chuàng)建Container組件
這是最后為每個組件連接Redux艳悔。
7.1 創(chuàng)建container component:AddTodo
7.2 創(chuàng)建container component:TodoList Container
7.3 創(chuàng)建container component:Filter Container
第八步:最終把他們結(jié)合到一起
import React from 'react' // ← Main React library
import { render } from 'react-dom' // ← Main react library
import { Provider } from 'react-redux' //← Bridge React and Redux
import { createStore } from 'redux' // ← Main Redux library
import todoApp from './reducers' // ← List of Reducers we created
//Import all components we created earlier
import AddTodo from '../containers/AddTodo'
import VisibleTodoList from '../containers/VisibleTodoList'
import Footer from './Footer' // ← This is a presentational component that contains 3 FilterLink Container comp
//Create Redux Store by passing it the reducers we created earlier.
let store = createStore(reducers)
render(
<Provider store={store}> ← The Provider component from react-redux injects the store to all the child components
<div>
<AddTodo />
<VisibleTodoList />
<Footer />
</div>
</Provider>,
document.getElementById(‘root’) //<-- Render to a div w/ id "root"
)