手挽手帶你學React入門四檔,用人話教你react-redux,理解redux架構,以及運用在react中。學完這一章,你就可以開始自己的react項目了。
之前在思否看到過某個大神的redux搭建 忘記了大神的名字 這里只記得內容了 憑借記憶和當時的學習路線寫下本文 隔空感謝
本人學習react-redux的時候遇到了很多坎,特別是不理解為什么這么用,這是什么東西,用來做什么。加上各種名詞讓人無法理解,所以這里我決定用人話,從原理走起,一步一步教大家使用react-redux。
開始之前本文開始之前,你需要先了解一些東西,當然我會在文中都一一教給大家。
首先你要會React的基礎(這是廢話)React上下文 context
對高階函數(shù)有一定的了解
有ES6基礎
滿足這三項我們開始往下看。
react官網(wǎng)說,context這個東西你可能永遠用不到,但是如果你使用了react-redux那么你還是無意間就使用到了它了。
那么它是個什么東西呢?你可以把它理解為全局的一個可以傳遞數(shù)據(jù)的東西,畢竟官方都沒給出對于context的定義。
我們直接來看看它是怎么樣來讓數(shù)據(jù)可以全局使用的
在使用 context之前 你需要先認識這幾個東西
首先需要
import PropTypes from "prop-types";
prop-types這個東西是一個幫你做類型檢測的 所以我們直接就使用好了
接下來是 childContextTypes 這個屬性 它是一個對象,里面規(guī)定我們要通過context來傳遞給下面的屬性名和類型 它通常在父組件中
然后是 getChildContext(){} 這是個規(guī)定好的方法 內部retrun一個對象 用來初始化 context的數(shù)據(jù)
最后是 contextTypes 這個屬性 它也是一個對象,里面規(guī)定我們要接收context來傳遞給下面的屬性名和類型 它通常在子組件中
好了 了解了的話 我們開始寫第一個 context了
// App.js import React,{Component} from "react" import PropTypes from "prop-types" //引入 export default class App extends Component { static childContextTypes = { //聲明要通過context傳遞的東西 propA: PropTypes.string, methodA: PropTypes.func } getChildContext () { //初始化context return { propA: "propA", methodA: () => "methodA" } } constructor(){ super() this.state={ } } componentWillMount(){ // console.log(hashHistory) } render() { return () } } // 為了展示效果定義子組件一 class Children extends Component{ constructor(){ super() this.state={ } } static contextTypes = { //規(guī)定要接收的東西 propA: PropTypes.string } render(){ console.log(this.context.methodA) // 因為沒有規(guī)定 所以現(xiàn)在是 undefined return( ) } } // 為了展示效果定義子組件二 ChildrenTwo 是 Children的子組件 但是卻通過context拿到了App組件拿過來的值 (越級傳遞) class ChildrenTwo extends Component{ static contextTypes={ methodA: PropTypes.func } constructor(){ super() this.state={ } } render(){ return({this.context.propA}
{/* 展示propA */}) } }{this.context.methodA()}
{/* 展示methodA */}
通俗一點來說 一個組件 通過 getChildContext方法來返回一個對象 這就是我們的context 經(jīng)過了 childContextTypes 聲明后 它的下層組件都可以通過 contextTypes 聲明。然后通過this.context獲取到內容并且使用了。
好了 context這里就講完了,大家把它放到你大腦的后臺里運行著,可能在這里你一頭霧水,講這個干毛。好的,我們接下來實現(xiàn)一個redux架構!
從零開始Redux我們創(chuàng)建一個HTML文件,就叫redux.html 所有東西我們寫在這一個html里面。
Document
上面的代碼,通過函數(shù)渲染把狀態(tài)內的東西渲染到了視圖中,但是,這里的狀態(tài)是暴露在外面的,任何一個地方都可以修改這個數(shù)據(jù)。這樣就不存在穩(wěn)定性可言了,我們想象一下,如果我們現(xiàn)在規(guī)定,你主動修改的state讓程序直接無視掉,只有你通過我給你的方法去修改,我才會認可這個狀態(tài)。因此 dispatch就出現(xiàn)了,這是修改數(shù)據(jù)唯一的地方。
Document
現(xiàn)在 你可以通過dispatch來修改state內容了,并且必須要按照它的聲明方式,和修改方式有規(guī)律地修改了。
是時候創(chuàng)建一個store了我們現(xiàn)在有了數(shù)據(jù),并且可以修改數(shù)據(jù)了,我們是不是可以創(chuàng)建我們的倉庫了?它的名字叫做 store ,當然,如果我們手動把這些東西塞進去,那就顯得太low了,使用函數(shù)作為一個工廠,幫我們生成這個那是極其舒坦的。
Document
到這里我們看到了一點Redux的雛形了,但是我們每次都要手動調用渲染,這是不是就非常地不爽。接下來我們要監(jiān)聽數(shù)據(jù)變化,讓它自己渲染數(shù)據(jù)。那么這個監(jiān)聽在哪里呢?沒錯store里面
設置數(shù)據(jù)監(jiān)聽大家可能想到 我們如果把渲染數(shù)據(jù)加入到dispatch里面不就好了嗎?沒錯,不過我們確實要在dispatch里面做文章。
function creatStore(state,stateChanger){ //這里我們創(chuàng)建一個函數(shù) 第一個參數(shù)是我們要用的狀態(tài)倉 第二個是我們自己做的dispatch const getState = () => state const dispatch = (action)=> { stateChanger(state,action) // 這里我們改變了狀態(tài) 然后我們需要刷新視圖 renderApp(state) } //state就是我們放進來的狀態(tài) action是我們調用時候傳進來 return{getState,dispatch} } const store = creatStore(state,dispatch) // 這里我們生成了store renderApp(store.getState()) // 渲染 store.dispatch({type:"UPDATE_HEAD_COLOR",color:"black"}) //改變state數(shù)值 store.dispatch({type:"UPDATE_HEAD_CONTEXT",context:"我變了"}) //改變state數(shù)值 // 現(xiàn)在我們可以監(jiān)聽數(shù)據(jù)變化了
但是這里我們遇到一個問題,這個creatStore只適用于我們當前的項目啊,不能夠通用啊。這該怎么辦呢?
其實簡單 我們動態(tài)傳入渲染的方法不就好了嗎 于是我們把代碼改成這樣
function creatStore(state,stateChanger){ //這里我們創(chuàng)建一個函數(shù) 第一個參數(shù)是我們要用的狀態(tài)倉 第二個是我們自己做的dispatch const getState = () => state const listenerList = [] const subscribe = (listener) => listenerList.push(listener) const dispatch = (action)=> { stateChanger(state,action) // 這里我們改變了狀態(tài) 然后我們需要刷新視圖 listenerList.map(item=>item()) } //state就是我們放進來的狀態(tài) action是我們調用時候傳進來 return{getState,dispatch,subscribe} } const store = creatStore(state,stateChanger) // 這里我們生成了store store.subscribe(()=>renderApp(store.getState())) renderApp(store.getState()) // 渲染 store.dispatch({type:"UPDATE_HEAD_COLOR",color:"black"}) //改變state數(shù)值 store.dispatch({type:"UPDATE_HEAD_CONTEXT",context:"我變了"}) //改變state數(shù)值 // 現(xiàn)在我們可以動態(tài)加入監(jiān)聽了性能優(yōu)化
寫到這里 問題又出現(xiàn)了,每次我們改動一個數(shù)據(jù) 或者數(shù)據(jù)沒有改動 只要是調用了 dispatch 我們就會觸發(fā)全部的刷新 我們加上console.log看一下
// 然后我們聲明三個渲染函數(shù) function renderMyHead(myHead){ function renderMyHead(myHead){ console.log("渲染了Head") var DOM = document.getElementById("myHead") DOM.innerHTML = myHead.context DOM.style.color = myHead.color } function renderMyBody(myBody){ console.log("渲染了Body") var DOM = document.getElementById("myBody") DOM.innerHTML = myBody.context DOM.style.color = myBody.color } function renderApp(state){ console.log("渲染了App") renderMyHead(state.myHead) renderMyBody(state.myBody) }
加上這些console以后 你會發(fā)現(xiàn) 我們只改變了head 但是 body也被重新渲染了 這就大大浪費了性能啊 我們怎么辦呢?沒錯 渲染之前檢測一下數(shù)據(jù)變沒變
不過我們先拋出一個問題
function renderMyHead(newMyHead,oldMyHead={}){ if(newMyHead==oldMyHead){ return } console.log("渲染了Head") var DOM = document.getElementById("myHead") DOM.innerHTML = newMyHead.context DOM.style.color = newMyHead.color } function renderMyBody(newMyBody,oldMyBody={}){ if(newMyBody===oldMyBody){ return } console.log("渲染了Body") var DOM = document.getElementById("myBody") DOM.innerHTML = newMyBody.context DOM.style.color = newMyBody.color } function renderApp (newState, oldState = {}) { if (newState === oldState) { return } renderMyHead(newState.myHead, oldState.myHead) renderContent(newState.myBody, oldState.myBody) } const store = creatStore(state,dispatch) // 這里我們生成了store let oldState = store.getState() store.subscribe(()=>{ const newState = store.getState() // 數(shù)據(jù)可能變化,獲取新的 state renderApp(newState,oldState) //把新舊數(shù)據(jù)傳禁區(qū) oldState = newState //記錄數(shù)據(jù) }) renderApp(store.getState()) // 渲染 store.dispatch({type:"UPDATE_HEAD_COLOR",color:"black"}) //改變state數(shù)值 store.dispatch({type:"UPDATE_HEAD_CONTEXT",context:"我變了"}) //改變state數(shù)值
好的 到這里 問題來了,我們寫這個有用嗎?
答案顯然易見 我們做這個等同于
let obj = {cc:1} let oldObj = obj obj.cc = 3 obj===oldObj // true
他們都指向了同一個地址呀 這有什么作用
所以我們現(xiàn)在要做的就是需要對 stateChanger內部的state返回模式進行改動,我們不再返回值,而是返回對象,當有對象返回的時候,我們的newState肯定就不等于oldState了,說到就做,嘗試一下
Document
到這里我們已經(jīng)搭建了自己的一個簡單的redux了,我們繼續(xù)往react-redux靠近
reducer我們上面寫 creatStore的時候 傳入了兩個參數(shù) state和 stateChanger 我們是不是可以把這兩個也合并到一起呢?沒問題 合并完了就是我們react-redux的reducer
// 我們就從stateChanger這個函數(shù)開始改 function stateChanger(state,action){ // 這里我們多加一個判斷 是否有state 如果沒有 我們就return一個 if(!state){ return{ myHead:{ color:"red", context:"我是腦袋" }, myBody:{ color:"blue", context:"我是身體" } } } switch (action.type){ case "UPDATE_HEAD_COLOR": return{ //這里我們使用ES6 不再去修改原來的state 而是 返回一個新的state 我們 creatStore里面的 dispatch方法也要跟著改動 ...state, myHead:{ ...state.myHead, color:action.color } } break; case "UPDATE_HEAD_CONTEXT": return{ ...state, myHead:{ ...state.myHead, context:action.context } } break; default: return{...state} break; } } function creatStore(stateChanger){ //現(xiàn)在我們不需要傳入state了 只需要傳入stateChanger 就好了 因為我們可以拿到它 let state = null const getState = () => state const listenerList = [] const subscribe = (listener) => listenerList.push(listener) const dispatch = (action)=> { state = stateChanger(state,action) //這里我們直接覆蓋原來是state // 這里我們改變了狀態(tài) 然后我們需要刷新視圖 listenerList.map(item=>item()) } dispatch({}) // 這里初始化 state // 我們一切都聲明完成 只需要調用一次 dispatch({}) 因為我們的state是null 所以 執(zhí)行了 state = stateChanger(state,action) 從而得到了我們stateChanger內部設置的state了 return{getState,dispatch,subscribe} } const store = creatStore(stateChanger) // 這里我們生成了store 并且不用傳入state了 只要把我們寫好的 stateChanger放進去就好了 // 這個 stateChanger 官方稱之為 reducer let oldStore = store.getState() //緩存舊數(shù)據(jù) store.subscribe(()=>{ let newState = store.getState() //獲得新數(shù)據(jù) renderApp(newState,oldStore) //調用比較渲染 oldStore = newState //數(shù)據(jù)緩存 }) renderApp(store.getState()) store.dispatch({type:"UPDATE_HEAD_COLOR",color:"black"}) //改變state數(shù)值 store.dispatch({type:"UPDATE_HEAD_CONTEXT",context:"我變了"}) //改變state數(shù)值 // 經(jīng)過我們一番改進 我們不再去調用Body的渲染了
到這里 你會突然發(fā)現(xiàn),自己竟然動手實現(xiàn)了一套redux!我們要和react結合起來 還需要一個過程。
總結在我們四檔上篇里面,從零開始搭建了一個自己的redux,這里面涉及到了太多高級的東西,大家需要好好消化,不理解的一定要留言提問~~
視頻制作中文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/101642.html
摘要:手挽手帶你學入門四檔用人話教你,理解架構,以及運用在中。學完這一章,你就可以開始自己的項目了。結合搭建基礎環(huán)境我們上一章講過了的原理,內部是有一個的,只有才可以控制它變化。 手挽手帶你學React入門四檔,用人話教你react-redux,理解redux架構,以及運用在react中。學完這一章,你就可以開始自己的react項目了。 視頻教程 上一篇我們自己實現(xiàn)了Redux,這一篇我們來...
摘要:當屬性是一個回調函數(shù)時,函數(shù)接收底層元素或類實例取決于元素的類型作為參數(shù)。 手挽手帶你學React入門第一期,帶你熟悉React的語法規(guī)則,消除對JSX的恐懼感,由于現(xiàn)在開發(fā)中都是使用ES6語法開發(fā)React,所以這次也使用ES6的模式進行教學,如果大家對ES6不熟悉的話,先去看看class相關內容吧,這里我也慢慢帶大家一步一步學會React。 視頻教程 視頻教程可移步我的個人博客:h...
摘要:手挽手帶你學入門二檔組件開發(fā)的開始,合理運用生命周期和組件,能夠讓你的開發(fā)變地流利又這篇文章帶你學會創(chuàng)建組件,運用組建。 手挽手帶你學React入門二檔,組件開發(fā)的開始,合理運用生命周期和組件,能夠讓你的開發(fā)變地流利又happy,這篇文章帶你學會創(chuàng)建組件,運用組建。學起來吧! React 組件生命周期 學習React,生命周期很重要,我們了解完生命周期的各個組件,對寫高性能組件會有很大...
摘要:這樣,我們用寫的就寫好了。真的假的大家可以看到,這些在插值表達式內的表達式直接返回了運行完成的結果,值得一提的是,差值表達式內的規(guī)則和標簽內的規(guī)則是類似的。 視頻教程 由于思否不能插入視頻,視頻請大家移步,http://www.henrongyi.top 什么是VUE VUE是一套用于構建用戶界面的漸進式框架,VUE并不是一個真正意義上的mvvm框架,它更傾向是一種數(shù)據(jù)驅動框架.所以我...
摘要:這樣,我們用寫的就寫好了。真的假的大家可以看到,這些在插值表達式內的表達式直接返回了運行完成的結果,值得一提的是,差值表達式內的規(guī)則和標簽內的規(guī)則是類似的。 視頻教程 由于思否不能插入視頻,視頻請大家移步,http://www.henrongyi.top 什么是VUE VUE是一套用于構建用戶界面的漸進式框架,VUE并不是一個真正意義上的mvvm框架,它更傾向是一種數(shù)據(jù)驅動框架.所以我...
閱讀 2195·2021-11-24 09:38
閱讀 3256·2021-11-08 13:27
閱讀 3099·2021-09-10 10:51
閱讀 3164·2019-08-29 12:20
閱讀 675·2019-08-28 18:28
閱讀 3472·2019-08-26 11:53
閱讀 2719·2019-08-26 11:46
閱讀 1529·2019-08-26 10:56