本系列目錄
點贊是美德 : )
今天我會快速的過一下 redux
知識,然後討論下 適用性
,最後為了讓大家能快速學習,簡化了官方的經典 Demo todo
,跟著我做一遍就行。
目標
- 了結什麼是 redux
- 什麼情況下適用
- 建立一個 redux 應用
- 安裝除錯外掛
閒話
redux
是一個很深的話題,先聊聊標準學習路線,開啟 redux.js.org/introductio…
筆者為了寫這文章,可是安靜的讀了一遍
首先 Introduction
7 篇
好像懂了什麼,不知道呢
然後 Base
6 篇
其實 Example: Todo List 這才是最有幫助的
又聽了視訊 Getting Started with Redux 視訊
好囉嗦啊
不過這也是學習的正確姿勢,總要看一遍官方文件,無奈官網設計的更像一個學術站點,而不是類庫工具一類的指導說明
好了好了~吐槽完畢,開始正文!
redux 是什麼, 可以不用麼
這張圖是 Flux 官網的,畫的很好。
Redux
是一種 資料的管理
方式,介面上發起各種操作 Action
,然後 Dispatcher
到 Store
更新狀態 State
,推送新狀態到檢視 View
好了,概念一句話說完了,來看看什麼情況下用 Redux
這個介面,如果用 React
來實現,在底部的 容器元件
要處理的業務有: 使用者登入、彈幕、主播資訊、視訊進度、道具、打賞、IM聊天、等等還有很多 而且隨著產品迭代,功能只會多
按我們之前的元件拆分,結構是合理,但是這個資料管理麻煩了,各種業務資料壓入子元件,各種業務事件返回到主容器元件,可能的程式碼結構如下
// 狀態
this.state = {
data1:...,
data2:...,
data3:...,
data...n:...
}
// 事件
function handelEvent1 = {...}
function handelEvent2 = {...}
function handelEvent3 = {...}
function handelEvent...n = {...}
// JSX
<主檢視元件>
<使用者資訊 data1={this.state.data1} handleEven1={...} handleEven2={...} handleEven...n={...} />
<主播資訊>
<基礎資料 data...n={...} handleEven...n={...} />
<頭像 data...n={...} handleEven...n={... />
<關注 data...n={...} handleEven...n={... />
<標籤 data...n={...} handleEven...n={... />
<熱度 data...n={...} handleEven...n={... />
...
</主播資訊>
<播放器 ...>
<... />
...
<播放器>
...
<...播放器/>
...
</主檢視元件>
複製程式碼
會發現元件套元件,父父子子的,完全沒法維護了,梳理這些關係就很費時間,而且容易錯誤
Redux
就是來解決這個問題的,每個元件只要執行自己的 Action
,不用返回到父容器
- 比如
Redux
在彈幕業務中就兩步:彈幕發出元件
執行發出彈幕動作Action
內容{ type: 'BARRAGE_SEND', text: '彈幕訊息' }
彈幕滾動元件
新彈幕資料被更新到彈幕滾動元件
但是也不好濫用,我看到有些簡單的 表單操作,竟然也套了 Redux
,完全沒必要,自己把握吧
動手建立一個 redux 應用
來個經典例子 todo
, 原版 Todo , 我這裡是精簡版,那麼我們開始
- 動圖效果
- 元件結構
第一步: 編寫 Action
介面上產生的操作
let nextTodoId = 0
export const addTodo = text => ({
type: 'ADD_TODO',
id: nextTodoId++,
text
})
export const toggleTodo = id => ({
type: 'TOGGLE_TODO',
id
})
複製程式碼
- 事件的格式
type
欄位必須有,表示做什麼操作type
值全域性唯一type
大寫定義- 其它欄位自由定義
第二步: 編寫 Reducers
事件對應的響應處理,處理完後返回新 state
const todos = (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 =>
todo.id === action.id ? {...todo, completed: !todo.completed} : todo
)
default:
return state
}
}
export default todos
複製程式碼
第三步: 合併 Reducers
使用 combineReducers
合併所有的處理過程
import { combineReducers } from 'redux'
import todos from './todos'
export default combineReducers({
todos
})
複製程式碼
假設你有其它業務, 如: 使用者 user
, 購物車 cart
import { combineReducers } from 'redux'
import todos from './todos'
import user from './user'
import cart from './cart'
export default combineReducers({
todos,
user,
cart
})
複製程式碼
第四步: 編寫元件 AddTodo
- 使用
connect
連線元件 - 使用
dispatch
方法派發事件
import React from 'react'
import { connect } from 'react-redux'
import { addTodo } from '../redux/actions'
const AddTodo = ({ dispatch }) => {
let input
return (
<div>
<form onSubmit={e => {
e.preventDefault()
if (!input.value.trim()) {
return
}
dispatch(addTodo(input.value))
input.value = ''
}}>
<input ref={node => input = node} />
<button type="submit">
Add Todo
</button>
</form>
</div>
)
}
export default connect()(AddTodo)
複製程式碼
dispatch(addTodo(input.value))
就發動了 Redux
到 Reducers
, 這時 state
更新了
第五步: 編寫元件 TodoList
再來個稍微複雜的
import React from 'react'
import {connect} from 'react-redux'
import {toggleTodo} from '../redux/actions'
const Todo = ({onClick, completed, text}) => (
<li
onClick={onClick}
style={{
textDecoration: completed ? 'line-through' : 'none'
}}
>
{text}
</li>
)
const TodoList = ({todos, toggleTodo}) => (
<ul>
{todos.map(todo => (
<Todo key={todo.id} {...todo} onClick={() => toggleTodo(todo.id)} />
))}
</ul>
)
const mapStateToProps = state => ({
todos: state.todos
})
const mapDispatchToProps = dispatch => ({
toggleTodo: id => dispatch(toggleTodo(id))
})
export default connect(mapStateToProps, mapDispatchToProps)(TodoList)
複製程式碼
還是 connect
方法 , 這裡重點講下
先看看官方定義 connect()
- 定義
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
複製程式碼
- 引數
名稱 | 說明 |
---|---|
mapStateToProps | store 繫結 state , 做更新的需要傳入 |
mapDispatchToProps | 繫結派發事件 event , 不傳的話預設 dispatch 物件, 就像上面的 AddTodo 元件 |
mergeProps | [mergeProps(stateProps, dispatchProps, ownProps): props] (Function): 合併屬性自定義,自己不傳的話預設 ·Object.assign ,我也是放空預設的 |
options | 一些選項,我沒怎麼在意,知道有就行 |
[mapStateToProps], [mapDispatchToProps]
這兩個用到的多,大家自己練習下
第六步: 容器元件
import React from 'react'
import AddTodo from './AddTodo'
import TodoList from './TodoList'
const App = () => (
<div>
<AddTodo />
<TodoList />
</div>
)
export default App
複製程式碼
這裡簡單,並列排放
最後: 適配 App
import React, {Component} from 'react'
import {createStore} from 'redux'
import {Provider} from 'react-redux'
import TodoApp from './todos/components/App'
import todoReducer from './todos/redux/reducers'
const store = createStore(todoReducer)
class BaseRedux extends Component {
render() {
return (
<Provider store={store}>
<TodoApp />
</Provider>
)
}
}
export default BaseRedux
複製程式碼
- 標準程式碼格式
createStore(reducers)
建立store
Provider
介面卡壓入store
物件, 子節點都受Redux
控制
除錯工具 Redux DevTools extension
動圖效果
1. 安裝 chrome 外掛
2. 配置程式碼
const store = createStore(
todoReducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
複製程式碼
createStore
時加入 window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
3. 開啟外掛
開啟 chrome
除錯工具,點選皮膚 Redux
codepen
https://codepen.io/ducafecat/pen/RyBEeK