Redux and React-redux 莞式教程(新手必看)
為了方便使用者使用,這裡就redux和react-redux 一併講究使用了,會了react-redux,redux也就沒問題了,只不過稍微封裝了一下
教程方式
-
我們先講理論,先明白 render、action、 store 、connect 、 dispatch 、{ mapStateToProps mapDispatchToProps },是什麼東西。
-
看完你可能有點懵,沒關係我有個簡單直接的
漲工資
的dom,跟著做一遍你就懂了
先 download 莞式教程 github.com/Chad97/My-n… —— 看了它你就一目瞭然了
一、
- 介紹 (有一定了解的自己跳 二 開始我的莞式教程)
對於大型的複雜應用來說,這兩方面恰恰是最關鍵的。因此,只用 React 沒法寫大型應用。 為了解決這個問題,2014年 Facebook 提出了 Flux 架構的概念,引發了很多的實現。2015年,Redux 出現,將 Flux 與函數語言程式設計結合一起,很短時間內就成為了最熱門的前端架構。
首先明確一點,Redux 是一個有用的架構,但不是非用不可。事實上,大多數情況,你可以不用它,只用 React 就夠了。 曾經有人說過這樣一句話。 "如果你不知道是否需要 Redux,那就是不需要它。"
—— Redux 的創造者 Dan Abramov
簡單說,如果你的UI層非常簡單,沒有很多互動,Redux 就是不必要的,用了反而增加複雜性。 使用者的使用方式非常簡單 使用者之間沒有協作 不需要與伺服器大量互動,也沒有使用 WebSocket 檢視層(View)只從單一來源獲取資料
Store
Store 就是儲存資料的地方,你可以把它看成一個容器。整個應用只能有一個 Store。 Redux 提供createStore這個函式,用來生成 Store。
import { createStore } from 'redux'; const store = createStore(fn); 複製程式碼
Action
State 的變化,會導致 View 的變化。但是,使用者接觸不到 State,只能接觸到 View。所以,State 的變化必須是 View 導致的。Action 就是 View 發出的通知,表示 State 應該要發生變化了。
Action 是一個物件。其中的type屬性是必須的,表示 Action 的名稱。其他屬性可以自由設定,社群有一個規範可以參考。
const action = { type: 'ADD_TODO', payload: 'Learn Redux' }; 複製程式碼
上面程式碼中,Action 的名稱是ADD_TODO,它攜帶的資訊是字串Learn Redux。 可以這樣理解,Action 描述當前發生的事情。改變 State 的唯一辦法,就是使用 Action。它會運送資料到 Store。
Action Creator
View 要傳送多少種訊息,就會有多少種 Action。如果都手寫,會很麻煩。可以定義一個函式來生成 Action,這個函式就叫 Action Creator。
const ADD_TODO = '新增 TODO'; function addTodo(text) { return { type: ADD_TODO, text } } 複製程式碼
const action = addTodo('Learn Redux'); 上面程式碼中,addTodo函式就是一個 Action Creator。
store.dispatch()
store.dispatch()是 View 發出 Action 的唯一方法。
import { createStore } from 'redux'; const store = createStore(fn); store.dispatch({ type: 'ADD_TODO', payload: 'Learn Redux' }); 複製程式碼
上面程式碼中,store.dispatch接受一個 Action 物件作為引數,將它傳送出去。 結合 Action Creator,這段程式碼可以改寫如下。
store.dispatch(addTodo('Learn Redux'));
Reducer
Store 收到 Action 以後,必須給出一個新的 State,這樣 View 才會發生變化。這種 State 的計算過程就叫做 Reducer。
Reducer 是一個函式,它接受 Action 和當前 State 作為引數,返回一個新的 State。
const reducer = function (state, action) { // ... return new_state; }; 複製程式碼
整個應用的初始狀態,可以作為 State 的預設值。下面是一個實際的例子。
const defaultState = 0; const reducer = (state = defaultState, action) => { switch (action.type) { case 'ADD': return state + action.payload; default: return state; } }; const state = reducer(1, { type: 'ADD', payload: 2 }); 複製程式碼
上面程式碼中,reducer函式收到名為ADD的 Action 以後,就返回一個新的 State,作為加法的計算結果。其他運算的邏輯(比如減法),也可以根據 Action 的不同來實現。
實際應用中,Reducer 函式不用像上面這樣手動呼叫,store.dispatch方法會觸發 Reducer 的自動執行。為此,Store 需要知道 Reducer 函式,做法就是在生成 Store 的時候,將 Reducer 傳入createStore方法。
import { createStore } from 'redux'; const store = createStore(reducer); 複製程式碼
上面程式碼中,createStore接受 Reducer 作為引數,生成一個新的 Store。以後每當store.dispatch傳送過來一個新的 Action,就會自動呼叫 Reducer,得到新的 State。
為什麼這個函式叫做 Reducer 呢?因為它可以作為陣列的reduce方法的引數。請看下面的例子,一系列 Action 物件按照順序作為一個數組。
const actions = [ { type: 'ADD', payload: 0 }, { type: 'ADD', payload: 1 }, { type: 'ADD', payload: 2 } ]; 複製程式碼
const total = actions.reduce(reducer, 0); // 3 上面程式碼中,陣列actions表示依次有三個 Action,分別是加0、加1和加2。陣列的reduce方法接受 Reducer 函式作為引數,就可以直接得到最終的狀態3。
store.subscribe()
Store 允許使用store.subscribe方法設定監聽函式,一旦 State 發生變化,就自動執行這個函式。
import { createStore } from 'redux'; const store = createStore(reducer); store.subscribe(listener); 複製程式碼
顯然,只要把 View 的更新函式(對於 React 專案,就是元件的render方法或setState方法)放入listen,就會實現 View 的自動渲染。
store.subscribe方法返回一個函式,呼叫這個函式就可以解除監聽。
let unsubscribe = store.subscribe(() => console.log(store.getState()) ); unsubscribe(); 複製程式碼
Store 的實現
上一節介紹了 Redux 涉及的基本概念,可以發現 Store 提供了三個方法。
store.getState() store.dispatch() store.subscribe() 複製程式碼
i mport { createStore } from 'redux';
let { subscribe, dispatch, getState } = createStore(reducer);
createStore方法還可以接受第二個引數,表示 State 的最初狀態。這通常是伺服器給出的。
let store = createStore(todoApp, window.STATE_FROM_SERVER) 上面程式碼中,window.STATE_FROM_SERVER就是整個應用的狀態初始值。注意,如果提供了這個引數,它會覆蓋 Reducer 函式的預設初始值。
下面是createStore方法的一個簡單實現,可以瞭解一下 Store 是怎麼生成的。
const createStore = (reducer) => { let state; let listeners = []; const getState = () => state; const dispatch = (action) => { state = reducer(state, action); listeners.forEach(listener => listener()); }; const subscribe = (listener) => { listeners.push(listener); return () => { listeners = listeners.filter(l => l !== listener); } }; dispatch({}); return { getState, dispatch, subscribe }; }; 複製程式碼

- 放一個簡單的計數器案例
const Counter = ({ value, onIncrement, onDecrement }) => ( <div> <h1>{value}</h1> <button onClick={onIncrement}>+</button> <button onClick={onDecrement}>-</button> </div> ); const reducer = (state = 0, action) => { switch (action.type) { case 'INCREMENT': return state + 1; case 'DECREMENT': return state - 1; default: return state; } }; const store = createStore(reducer); const render = () => { ReactDOM.render( <Counter value={store.getState()} onIncrement={() => store.dispatch({type: 'INCREMENT'})} onDecrement={() => store.dispatch({type: 'DECREMENT'})} />, document.getElementById('root') ); }; render(); store.subscribe(render); 複製程式碼
二、
先 download 莞式教程 github.com/Chad97/My-n… —— 看了它你就一目瞭然了
這一章 我們簡單的講一下 react-redux ,就開始我的莞試教程
我們的react-redux 為方便我們使用,內建了幾個元件,非常簡單,非常好用 —— connect()
mapStateToProps
mapDispatchToProps
<Provider></Provider>
- 好下面讓我們來單刀直入~
reacr-redux 之漲工資
npx redux-dom
新建一個react專案 低版本 nodejs 用 create-app
也可以
- 在
src
資料夾下面建立一個 store ,store裡面建立一個reducer.js
如下:
import React from 'react'; import { connect } from 'react-redux'; const tiger = 30000//建立工資state //這是action const increase = { type: '漲工資' } const decrease = { type: '扣工資' } consttaxes = { type: '繳稅' } //這是reducer const reducer = (state = tiger, action) => { switch (action.type) { case '漲工資': return state += 100; case '扣工資': return state -= 100; case '繳稅' : return state - ((state - 5000) *0.01) default: return state; } } export default reducer 複製程式碼
然後進入我們的 index.js
import React, { Component } from 'react'; import ReactDOM from 'react-dom'; import App from './App' import { createStore } from 'redux' import { Provider } from 'react-redux' import reducer from './store/reducer' //建立store const store = createStore(reducer); ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') ) 複製程式碼
- 講一下 Provider 這個元件,他是react-redux 中提供的一個元件
connect方法生成容器元件以後,需要讓容器元件拿到state物件,才能生成 UI 元件的引數。 一種解決方法是將state物件作為引數,傳入容器元件。但是,這樣做比較麻煩,尤其是容器元件可能在很深的層級,一級級將state傳下去就很麻煩。 React-Redux 提供Provider元件,可以讓容器元件拿到state
記住 他就是 通過 class 的connect 實現的,能讓Provider 的元件的子孫元件都拿到掛載的store,莞式教程簡單粗暴,要看實現原理 去看es6去
好了現在讓我去app.js裡面,去實現漲工資吧 哈哈~~~
import React, { Component } from 'react'; import { connect } from 'react-redux'; import Home from './components/Home' class App extends Component { componentDidMount () { // console.log(this.props) } render() { const { PayIncrease, PayDecrease } = this.props return ( <div className="App"> <h2>當月工資為{this.props.tiger}</h2> <button onClick={PayIncrease}>升職加薪</button> <button onClick={PayDecrease}>遲到罰款</button> <Home /> </div> ); } } //需要渲染什麼資料 function mapStateToProps(state) { return { tiger: state } } //需要觸發什麼行為 function mapDispatchToProps(dispatch) { return { PayIncrease: () => dispatch({ type: '漲工資' }), PayDecrease: () => dispatch({ type: '扣工資' }) } } export default App = connect(mapStateToProps, mapDispatchToProps)(App) 複製程式碼
-
connect
React-Redux 提供connect方法,用於從 UI 元件生成容器元件。connect的意思,就是將這兩種元件連起來。
記住,要想讓元件拿到store 就得用 connect () 這個方法 來連結,你可以理解架橋,別想太多,簡單粗暴 用多了你就知道原理了。
列印工資
好現在讓我在 src/components
羨慕新建一個 Home.jsx 元件
,漲完工資是不是要列印工資
import React from 'react'; import { connect } from 'react-redux' import Counter from './Counter ' class Home extends React.Component { constructor (props, context){ super() this.state = { } } mycl = () => { alert(this.props.tiger) } render() { return ( <div> <hr /> <br /> <h1>home 元件</h1> <button onClick= {this.mycl}>列印目前工資</button> <hr /> <br /> <Counter /> </div> ); } componentDidMount () { console.log(this.props) } } //需要渲染什麼資料 function mapStateToProps(state) { return { tiger: state } } //需要觸發什麼行為 function mapDispatchToProps(dispatch) { return { PayIncrease: () => dispatch({ type: '漲工資' }), PayDecrease: () => dispatch({ type: '扣工資' }) } } export default connect(mapStateToProps, mapDispatchToProps)(Home) 複製程式碼
繳稅
漲完工資,工資那麼高是不是要繳稅拉~~
import React from 'react'; import { connect } from 'react-redux'; class Counterextends React.Component { constructor(props) { super(props); this.state = { }; } render() { const { Paytaxes } = this.props return ( <div> <h2>稅後計算</h2> <button onClick={ Paytaxes } >繳稅</button> <p>{this.props.tiger}</p> </div> ); } } //需要渲染什麼資料 function mapStateToProps(state) { return { tiger: state } } //需要觸發什麼行為 function mapDispatchToProps(dispatch) { return { PayIncrease: () => dispatch({ type: '漲工資' }), PayDecrease: () => dispatch({ type: '扣工資' }), Paytaxes: () => dispatch({ type: '繳稅' }), } } export default connect(mapStateToProps, mapDispatchToProps)(Counter) 複製程式碼