摘要:要實現(xiàn)先搞清楚的就是這么一個東西,某個組件只要往自己的里面放了某些狀態(tài),這個組件之下的所有子組件都直接訪問這個狀態(tài)而不需要通過中間組件的傳遞。
1.要實現(xiàn)redux,先搞清楚context
React.js 的 context 就是這么一個東西,某個組件只要往自己的 context 里面放了某些狀態(tài),這個組件之下的所有子組件都直接訪問這個狀態(tài)而不需要通過中間組件的傳遞。一個組件的 context 只有它的子組件能夠訪問,它的父組件是不能訪問到的
//現(xiàn)在我們修改 Index,讓它往自己的 context 里面放一個 themeColor class Index extends Component { static childContextTypes = {//要給組件設(shè)置 context,那么 childContextTypes 是必寫的 themeColor: PropTypes.string } constructor () { super() this.state = { themeColor: "red" } } getChildContext () { return { themeColor: this.state.themeColor } } render () { return () } }
//子組件直接獲取context里面的東西 //子組件要獲取 context 里面的內(nèi)容的話,就必須寫 contextTypes 來聲明和驗證你需要獲取的狀態(tài)的類型, //它也是必寫的 class Title extends Component { static contextTypes = { themeColor: PropTypes.string } render () { return (2.實現(xiàn)共享狀態(tài)優(yōu)化React.js
) } }
context 打破了組件和組件之間通過 props 傳遞數(shù)據(jù)的規(guī)范,極大地增強了組件之間的耦合性。而且,就如全局變量一樣,context 里面的數(shù)據(jù)能被隨意接觸就能被隨意修改,每個組件都能夠改 context 里面的內(nèi)容會導(dǎo)致程序的運行不可預(yù)料,這時我們就需要規(guī)范對共享狀態(tài)的修改
1.假設(shè)使用的是一個共享狀態(tài) appState,每個人都可以修改它
2.所有對共享狀態(tài)的操作都是不可預(yù)料的(某個模塊 appState.title = null 你一點意見都沒有),出現(xiàn)問題的時候 debug 起來就非常困難
3.如果所有對數(shù)據(jù)的操作必須通過 dispatch 函數(shù)。它接受一個參數(shù) action,這個 action 是一個普通的 JavaScript 對象,里面必須包含一個 type 字段來聲明你到底想干什么,那就好操作了
4.我們可以把appState 和 dispatch抽離出來結(jié)合到一起形成store,構(gòu)建一個函數(shù) createStore,用來專門生產(chǎn)這種 state 和 dispatch 的集合
/*createStore 接受兩個參數(shù),一個是表示應(yīng)用程序狀態(tài)的 state;另外一個是 stateChanger, 它來描述應(yīng)用程序狀態(tài)會根據(jù) action 發(fā)生什么變化*/ function createStore (state, stateChanger) { const getState = () => state const dispatch = (action) => stateChanger(state, action) return { getState, dispatch } } renderApp(store.getState()) // 首次渲染頁面 store.dispatch({ type: "UPDATE_TITLE_TEXT", text: "《React.js 》" }) // 修改標(biāo)題文本 store.dispatch({ type: "UPDATE_TITLE_COLOR", color: "blue" }) // 修改標(biāo)題顏色 renderApp(store.getState()) // 把新的數(shù)據(jù)渲染到頁面上3.實現(xiàn)Redux數(shù)據(jù)刷新優(yōu)化
1.細上面代碼中更改數(shù)據(jù)后需要手動調(diào)用renderapp刷新,這里我們可以用觀察者模式優(yōu)化刷新
function createStore (state, stateChanger) { const listeners = [] const subscribe = (listener) => listeners.push(listener) const getState = () => state const dispatch = (action) => { stateChanger(state, action) listeners.forEach((listener) => listener())//dispatch后調(diào)用我傳入subscribe的刷新方式 } return { getState, dispatch, subscribe } } //可以用同一個APPstate去渲染不同的頁面 const store = createStore(appState, stateChanger) store.subscribe(() => renderApp(store.getState())) store.subscribe(() => renderApp2(store.getState())) store.subscribe(() => renderApp3(store.getState()))
2.避免重復(fù)渲染優(yōu)化 這里直接引用胡子大叔的優(yōu)化
function createStore (state, stateChanger) { const listeners = [] const subscribe = (listener) => listeners.push(listener) const getState = () => state const dispatch = (action) => { state = stateChanger(state, action) // 覆蓋原對象 listeners.forEach((listener) => listener()) } return { getState, dispatch, subscribe } } function renderApp (newAppState, oldAppState = {}) { // 防止 oldAppState 沒有傳入,所以加了默認(rèn)參數(shù) oldAppState = {} if (newAppState === oldAppState) return // 數(shù)據(jù)沒有變化就不渲染了 console.log("render app...") renderTitle(newAppState.title, oldAppState.title) renderContent(newAppState.content, oldAppState.content) } function renderTitle (newTitle, oldTitle = {}) { if (newTitle === oldTitle) return // 數(shù)據(jù)沒有變化就不渲染了 console.log("render title...") const titleDOM = document.getElementById("title") titleDOM.innerHTML = newTitle.text titleDOM.style.color = newTitle.color } function renderContent (newContent, oldContent = {}) { if (newContent === oldContent) return // 數(shù)據(jù)沒有變化就不渲染了 console.log("render content...") const contentDOM = document.getElementById("content") contentDOM.innerHTML = newContent.text contentDOM.style.color = newContent.color } let appState = { title: { text: "React.js 小書", color: "red", }, content: { text: "React.js 小書內(nèi)容", color: "blue" } } function stateChanger (state, action) { switch (action.type) { case "UPDATE_TITLE_TEXT": return { // 構(gòu)建新的對象并且返回 ...state, title: { ...state.title, text: action.text } } case "UPDATE_TITLE_COLOR": return { // 構(gòu)建新的對象并且返回 ...state, title: { ...state.title, color: action.color } } default: return state // 沒有修改,返回原來的對象 } } const store = createStore(appState, stateChanger) let oldState = store.getState() // 緩存舊的 state store.subscribe(() => { const newState = store.getState() // 數(shù)據(jù)可能變化,獲取新的 state renderApp(newState, oldState) // 把新舊的 state 傳進去渲染 oldState = newState // 渲染完以后,新的 newState 變成了舊的 oldState,等待下一次數(shù)據(jù)變化重新渲染 }) renderApp(store.getState()) // 首次渲染頁面 store.dispatch({ type: "UPDATE_TITLE_TEXT", text: "《React.js 小書》" }) // 修改標(biāo)題文本 store.dispatch({ type: "UPDATE_TITLE_COLOR", color: "blue" }) // 修改標(biāo)題顏色4.Reducer
其實 appState 和 stateChanger 可以合并到一起去
1將appstate放入statechanger
function stateChanger (state, action) { if (!state) { return { title: { text: "React.js 小書", color: "red", }, content: { text: "React.js 小書內(nèi)容", color: "blue" } } } switch (action.type) { case "UPDATE_TITLE_TEXT": return { ...state, title: { ...state.title, text: action.text } } case "UPDATE_TITLE_COLOR": return { ...state, title: { ...state.title, color: action.color } } default: return state } }
2.creactstore的參數(shù)就會被優(yōu)化為一個
function createStore (stateChanger) { let state = null const listeners = [] const subscribe = (listener) => listeners.push(listener) const getState = () => state const dispatch = (action) => { state = stateChanger(state, action) listeners.forEach((listener) => listener()) } dispatch({}) // 初始化 state return { getState, dispatch, subscribe } }
3.最后我們規(guī)定createStore參數(shù)的名字為reducer,且reducer是一個純函數(shù)
reducer 是不允許有副作用的。你不能在里面操作 DOM,也不能發(fā) Ajax 請求,更不能直接修改 state,它要做的僅僅是 —— 初始化和計算新的 state
// 定一個 reducer function reducer (state, action) { /* 初始化 state 和 switch case */ } // 生成 store const store = createStore(reducer) // 監(jiān)聽數(shù)據(jù)變化重新渲染頁面 store.subscribe(() => renderApp(store.getState())) // 首次渲染頁面 renderApp(store.getState()) // 后面可以隨意 dispatch 了,頁面自動更新 store.dispatch(...)5.React-redux中的store和context
React.js 的 context 中提出,我們可用把共享狀態(tài)放到父組件的 context 上,這個父組件下所有的組件都可以從 context 中直接獲取到狀態(tài)而不需要一層層地進行傳遞了,但組件對其的改動會讓context不可預(yù)料。 store 的數(shù)據(jù)不是誰都能修改,而是約定只能通過 dispatch 來進行修改,這樣的話每個組件既可以去 context 里面獲取 store 從而獲取狀態(tài),又不用擔(dān)心它們亂改數(shù)據(jù)了,所以將store和context結(jié)合起來
1.構(gòu)建自己的React-redux
import React, { Component } from "react" import PropTypes from "prop-types" import ReactDOM from "react-dom" import Header from "./Header" import Content from "./Content" import "./index.css" function createStore (reducer) { let state = null const listeners = [ const subscribe = (listener) => listeners.push(listener) const getState = () => state//這是函數(shù)表達式 調(diào)用它時state已經(jīng)初始化了 const dispatch = (action) => { state = reducer(state, action) listeners.forEach((listener) => listener()) } dispatch({}) // 初始化 state return { getState, dispatch, subscribe } } const themeReducer = (state, action) => { if (!state) return { themeColor: "red" } switch (action.type) { case "CHANGE_COLOR": return { ...state, themeColor: action.themeColor } default: return state } } const store = createStore(themeReducer) class Index extends Component { static childContextTypes = { store: PropTypes.object } getChildContext () { return { store }//將store放入context } render () { return () } }
2.子組件獲取context中的配置
class Header extends Component { static contextTypes = { store: PropTypes.object } constructor () { super() this.state = { themeColor: "" } } componentWillMount () { this._updateThemeColor() } _updateThemeColor () { const { store } = this.context//解構(gòu)賦值取出來 const state = store.getState() this.setState({ themeColor: state.themeColor })//放到state中來用 } render () { return (React.js 小書
) } }
3.用dispatch去改變配置刷新頁面
//首先配置監(jiān)聽函數(shù)的刷新模式 componentWillMount () { const { store } = this.context this._updateThemeColor()//獲取默認(rèn)數(shù)據(jù)加載 store.subscribe(() => this._updateThemeColor())//dispatch數(shù)據(jù)更改后加載 } //觸發(fā)事件 handleSwitchColor (color) { const { store } = this.context store.dispatch({ type: "CHANGE_COLOR", themeColor: color }) }6.React-redux與組件拆分開,讓組件無污染可復(fù)用性強
可以把一些可復(fù)用的邏輯放在高階組件當(dāng)中,高階組件包裝的新組件和原來組件之間通過 props 傳遞信息,減少代碼的重復(fù)程度,我們需要高階組件幫助我們從 context 取數(shù)據(jù),然后用高階組件把它們包裝一層,高階組件和 context 打交道,把里面數(shù)據(jù)取出來通過 props 傳給 Dumb 組件
1.這個高階組件起名字叫 connect,因為它把 Dumb 組件和 context 連接
2.每個傳進去的組件需要 store 里面的數(shù)據(jù)都不一樣的,所以除了給高階組件傳入 Dumb 組件以外,還需要告訴高級組件我們需要什么數(shù)據(jù)
import React, { Component } from "react" import PropTypes from "prop-types" //connect 現(xiàn)在是接受一個參數(shù) mapStateToProps,然后返回一個函數(shù),這個返回的函數(shù)才是高階組件 export const connect = (mapStateToProps) => (WrappedComponent) => { class Connect extends Component { static contextTypes = { store: PropTypes.object } render () { const { store } = this.context let stateProps = mapStateToProps(store.getState()) // {...stateProps} 意思是把這個對象里面的屬性全部通過 `props` 方式傳遞進去 return} } return Connect } ---------- //mapStateToProps為傳入數(shù)據(jù)的方式 const mapStateToProps = (state) => { return { themeColor: state.themeColor } } Header = connect(mapStateToProps)(Header) //這里的mapStateToprops在connect里面執(zhí)行并把獲取的數(shù)據(jù)放到header的props中
3.除了傳遞數(shù)據(jù)我們還需要高階組件來 dispatch
const mapDispatchToProps = (dispatch) => { return { onSwitchColor: (color) => { dispatch({ type: "CHANGE_COLOR", themeColor: color }) } } }
4.結(jié)合起來構(gòu)建Connect
export const connect = (mapStateToProps, mapDispatchToProps) => (WrappedComponent) => { class Connect extends Component { static contextTypes = { store: PropTypes.object } constructor () { super() this.state = { allProps: {} } } componentWillMount () { const { store } = this.context this._updateProps() store.subscribe(() => this._updateProps()) } _updateProps () { const { store } = this.context let stateProps = mapStateToProps ? mapStateToProps(store.getState(), this.props) : {} // 防止 mapStateToProps 沒有傳入 let dispatchProps = mapDispatchToProps ? mapDispatchToProps(store.dispatch, this.props) : {} // 防止 mapDispatchToProps 沒有傳入 this.setState({ allProps: { ...stateProps, ...dispatchProps, ...this.props } }) } render () { return} } return Connect }
5.剝離出index.js
class Index extends Component { static childContextTypes = { store: PropTypes.object } getChildContext () { return { store } }//這些都是污染需要剝離 render () { return () } }
6.Provider
//將index中污染部分放入Provider,再用成為index的父組件 export class Provider extends Component { static propTypes = { store: PropTypes.object, children: PropTypes.any } static childContextTypes = { store: PropTypes.object } getChildContext () { return { store: this.props.store } } render () { return ({this.props.children}) } }
... // 頭部引入 Provider import { Provider } from "./react-redux" ... // 刪除 Index 里面所有關(guān)于 context 的代碼 class Index extends Component { render () { return () } } // 把 Provider 作為組件樹的根節(jié)點 ReactDOM.render(, document.getElementById("root") )
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/108899.html
摘要:寫在前頭簡介隨著單頁應(yīng)用開發(fā)日趨復(fù)雜,需要管理比任何時候都要多的狀態(tài)。如果一個的變化會引起另一個變化,那么當(dāng)變化時,就可能引起對應(yīng)以及另一個的變化,依次地,可能會引起另一個的變化。一些庫如試圖在視圖層禁止異步和直接操作來解決這個問題。 寫在前頭 redux 簡介 ? 隨著 JavaScript 單頁應(yīng)用開發(fā)日趨復(fù)雜,JavaScript 需要管理比任何時候都要多的 state (...
摘要:要實現(xiàn)先搞清楚的就是這么一個東西,某個組件只要往自己的里面放了某些狀態(tài),這個組件之下的所有子組件都直接訪問這個狀態(tài)而不需要通過中間組件的傳遞。 1.要實現(xiàn)redux,先搞清楚context React.js 的 context 就是這么一個東西,某個組件只要往自己的 context 里面放了某些狀態(tài),這個組件之下的所有子組件都直接訪問這個狀態(tài)而不需要通過中間組件的傳遞。一個組件的 co...
摘要:日常項目直接使用是完全沒有問題的,可是隨著項目的日益壯大,組件數(shù)量的逐漸增長,組件之間的嵌套使得數(shù)據(jù)的管理越來越繁重。最后數(shù)據(jù)保存進了中的,頁面也會根據(jù)的改變自動更新。 以下文章均為個人近期所學(xué)心得,自學(xué)react、redux,逐漸找到自己的方向,現(xiàn)將自己的方向方式寫出來,以供大家學(xué)習(xí)參考,肯定會有不足,歡迎批評指正。 日常項目直接使用react是完全沒有問題的,可是隨著項目的日益壯大...
摘要:但這并不是最佳的方式。最佳的方式是使用提供的和方法。也就是說,與的與完全無關(guān)。另外,如果使用對做屬性類型檢查,方法和方法為添加的屬性是存在的。注意,的變化不會引起上述過程,默認(rèn)在組件的生命周期中是固定的。 轉(zhuǎn)載注: 本文作者是淘寶前端團隊的葉齋。筆者非常喜歡這篇文章,故重新排版并轉(zhuǎn)載到這里,同時也加入了一些自己的體會。 原文地址:http://taobaofed.org/blog/...
閱讀 964·2023-04-26 02:56
閱讀 9437·2021-11-23 09:51
閱讀 1850·2021-09-26 10:14
閱讀 2980·2019-08-29 13:09
閱讀 2154·2019-08-26 13:29
閱讀 571·2019-08-26 12:02
閱讀 3562·2019-08-26 10:42
閱讀 3000·2019-08-23 18:18