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