欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

React手寫redux過程分步講解

 更新時(shí)間:2022年12月19日 09:17:14   投稿:zhxr  
這篇文章主要介紹了React手寫redux過程,目前redux在react中使用是最多的,所以我們需要將之前編寫的redux代碼,融入到react當(dāng)中去,本文給大家詳細(xì)講解,需要的朋友可以參考下

提起 Redux 我們想到最多的應(yīng)該就是 React-redux 這個(gè)庫(kù),可是實(shí)際上 Redux 和 React-redux 并不是同一個(gè)東西, Redux 是一種架構(gòu)模式,源于 Flux。 React-redux 是 Redux 思想與 React 結(jié)合的一種具體實(shí)現(xiàn)。

在我們使用 React 的時(shí)候,常常會(huì)遇到組件深層次嵌套且需要值傳遞的情況,如果使用 props 進(jìn)行值的傳遞,顯然是非常痛苦的。為了解決這個(gè)問題,React 為我們提供了原生的 context API,但我們用的最多的解決方案卻是使用 React-redux 這個(gè)基于 context API 封裝的庫(kù)。

本文并不介紹 React-redux 的具體用法,而是通過一個(gè)小例子,來了解下什么是 redux。

好了,現(xiàn)在我們言歸正傳,來實(shí)現(xiàn)我們自己的 redux。

一、最初

首先,我們用 creat-react-app 來創(chuàng)建一個(gè)項(xiàng)目,刪除 src 下冗余部分,只保留 index.js,并修改 index.html 的 DOM 結(jié)構(gòu):

# index.html
<div id="root">
  <div id="head"></div>
  <div id="body"></div>
</div>

我們?cè)?index.js 中創(chuàng)建一個(gè)對(duì)象,用它來儲(chǔ)存、管理我們整個(gè)應(yīng)用的數(shù)據(jù)狀態(tài),并用渲染函數(shù)把數(shù)據(jù)渲染在頁面:

const appState = {
  head: {
    text: '我是頭部',
    color: 'red'
  },
  body: {
    text: '我是body',
    color: 'green'
  }
}
function renderHead (state){
  const head = document.getElementById('head')
  head.innerText = state.head.text;
  head.style.color = state.head.color;
}
function renderBody (state){
  const body = document.getElementById('body')
  body.innerText = state.body.text;
  body.style.color = state.body.color;
}
function renderApp (state){
  renderHead(state);
  renderBody(state);
}
renderApp(appState);

此時(shí)運(yùn)行代碼,打開頁面,我們可以看到,在 head 中已經(jīng)出現(xiàn)了紅色字體的‘我是頭部’,在 body 中出現(xiàn)了綠色字體的‘我是body’。

如果我們把 head 和 body 看作是 root 中的兩個(gè)組件,那么我們已經(jīng)實(shí)現(xiàn)了一個(gè)全局唯一的 state 。這個(gè) state 是全局共享的,隨處可調(diào)用的。

我們可以修改 head 的渲染函數(shù),來看下效果:

function renderHead (state){
  const head = document.getElementById('head')
  head.innerText = state.head.text + '--' + state.body.text;
  head.style.color = state.head.color;
  state.body.text = '我是經(jīng)過 head 修改后的 body';
}

我們看到,在 head 渲染函數(shù)中,我們不僅可以取用 body 屬性的值,還可以改變他的值。這樣就存在一個(gè)嚴(yán)重的問題,因?yàn)?state 是全局共用的,一旦在一個(gè)地方改變了 state 的值,那么,所有用到這個(gè)值的組件都將受到影響,而且這個(gè)改變是不可預(yù)期的,顯然給我們的代碼調(diào)試增加了難度系數(shù),這樣的結(jié)果是我們不愿意看到的!

二、dispatch

現(xiàn)在看來,在我們面前出現(xiàn)了一個(gè)矛盾:我們需要數(shù)據(jù)共享,但共享數(shù)據(jù)被任意的修改又會(huì)造成不可預(yù)期的問題!

為了解決這個(gè)矛盾,我們需要一個(gè)管家,專門來管理共享數(shù)據(jù)的狀態(tài),任何對(duì)共享數(shù)據(jù)的操作都要通過他來完成,這樣,就避免了隨意修改共享數(shù)據(jù)帶來的不可預(yù)期的危害!

我們重新定義一個(gè)函數(shù),用這個(gè)函數(shù)充當(dāng)我們的管家,來對(duì)我們的共享數(shù)據(jù)進(jìn)行管理:

function dispatch(state, action) {
  switch (action.type) {
    case 'HEAD_COLOR':
      state.head.color = action.color
      break
    case 'BODY_TEXT':
      state.body.text = action.text
      break
    default:
      break
  }
}

我們來重新修改head 的渲染函數(shù):

function renderHead (state){
  const head = document.getElementById('head')
  head.innerText = state.head.text + '--' + state.body.text;
  head.style.color = state.head.color;
  dispatch(state, { type: 'BODY_TEXT', text: '我是 head 經(jīng)過調(diào)用 dispatch 修改后的 body' })
}

dispatch 函數(shù)接收兩個(gè)參數(shù),一個(gè)是需要修改的 state ,另一個(gè)是修改的值。這時(shí),雖然我們依舊修改了 state ,但是通過 dispatch 函數(shù),我們使這種改變變得可控,因?yàn)槿魏胃淖?state 的行為,我們都可以在 dispatch 中找到改變的源頭。

這樣,我們似乎已經(jīng)解決了之前的矛盾,我們創(chuàng)建了一個(gè)全局的共享數(shù)據(jù),而且嚴(yán)格的把控了任何改變這個(gè)數(shù)據(jù)的行為。

然而,在一個(gè)文件中,我們既要保存 state, 還要維護(hù)管家函數(shù) dispatch,隨著應(yīng)用的越來越復(fù)雜,這個(gè)文件勢(shì)必會(huì)變得冗長(zhǎng)繁雜,難以維護(hù)。

現(xiàn)在,我們把 state 和 dispatch 單獨(dú)抽離出來:

  • 用一個(gè)文件單獨(dú)保存 state
  • 用另一個(gè)文件單獨(dú)保存 dispatch 中修改 state 的對(duì)照關(guān)系 changeState
  • 最后再用一個(gè)文件,把他們結(jié)合起來,生成全局唯一的 store

這樣,不僅使單個(gè)文件變得更加精簡(jiǎn),而且在其他的應(yīng)用中,我們也可以很方便的復(fù)用我們這套方法,只需要傳入不同應(yīng)用的 state 和修改 state 的對(duì)應(yīng)邏輯 stateChange,就可以放心的通過調(diào)用 dispatch 方法,對(duì)數(shù)據(jù)進(jìn)行各種操作了:參考前端手寫面試題詳細(xì)解答

# 改變我們的目錄結(jié)構(gòu),新增 redux 文件夾
+ src
++ redux
--- state.js // 儲(chǔ)存應(yīng)用數(shù)據(jù)狀態(tài)
--- storeChange.js //  維護(hù)一套修改 store 的邏輯,只負(fù)責(zé)計(jì)算,返回新的 store
--- createStore.js // 結(jié)合 state 和 stateChange , 創(chuàng)建 store ,方便任何應(yīng)用引用 
--index.js 

## 修改后的各個(gè)文件
# state.js -- 全局狀態(tài)
export const state = {
  head: {
    text: '我是頭部',
    color: 'red'
  },
  body: {
    text: '我是body',
    color: 'green'
  }
}
# storeChange.js -- 只負(fù)責(zé)計(jì)算,修改 store
export const storeChange = (store, action) => {
  switch (action.type) {
    case 'HEAD_COLOR':
      store.head.color = action.color
      break
    case 'BODY_TEXT':
      store.body.text = action.text
      break
    default:
      break
  }
}
# createStore.js -- 創(chuàng)建全局 store
export const createStore = (state, storeChange) => {
  const store = state || {};
  const dispatch = (action) => storeChange(store, action);
  return { store, dispatch }
}
# index.js 
import { state } from './redux/state.js';
import { storeChange } from './redux/storeChange.js';
import { createStore } from './redux/createStore.js';
const { store, dispatch } = createStore(state, storeChange)
function renderHead (state){
  const head = document.getElementById('head')
  head.innerText = state.text;
  head.style.color = state.color;
}
function renderBody (state){
  const body = document.getElementById('body')
  body.innerText = state.text;
  body.style.color = state.color;
}
function renderApp (store){
  renderHead(store.head);
  renderBody(store.body);
}
// 首次渲染
renderApp(store);

通過以上的文件拆分,我們看到,不僅使單個(gè)文件更加精簡(jiǎn),文件的職能也更加明確:

  • 在 state 中,我們只保存我們的共享數(shù)據(jù)
  • 在 storeChange 中,我們來維護(hù)改變 store 的對(duì)應(yīng)邏輯,計(jì)算出新的 store
  • 在 createStore 中,我們創(chuàng)建 store
  • 在 index.js 中,我們只需要關(guān)心相應(yīng)的業(yè)務(wù)邏輯

三、subscribe

一切似乎都那么美好,可是當(dāng)我們?cè)谑状武秩竞笳{(diào)用 dispatch 修改

store 時(shí),我們發(fā)現(xiàn),雖然數(shù)據(jù)被改變了,可是頁面并沒有刷新,只有在 dispatch 改變數(shù)據(jù)后,重新調(diào)用 renderApp() 才能實(shí)現(xiàn)頁面的刷新。

// 首次渲染
renderApp(store);
dispatch({ type: 'BODY_TEXT', text: '我是調(diào)用 dispatch 修改的 body' }) // 修改數(shù)據(jù)后,頁面并沒有自動(dòng)刷新
renderApp(store);  // 重新調(diào)用 renderApp 頁面刷新

這樣,顯然并不能達(dá)到我們的預(yù)期,我們并不想在每次改變數(shù)據(jù)后手動(dòng)的刷新頁面,如果能在改變數(shù)據(jù)后,自動(dòng)進(jìn)行頁面的刷新,當(dāng)然再好不過了!

如果直接把 renderApp 寫在 dispatch 里,顯然是不太合適的,這樣我們的 createStore 就失去了通用性。

我們可以在 createStore 中新增一個(gè)收集數(shù)組,把 dispatch 調(diào)用后需要執(zhí)行的方法統(tǒng)一收集起來,然后再循環(huán)執(zhí)行,這樣,就保證了 createStore 的通用性:

# createStore
export const createStore = (state, storeChange) => {
  const listeners = [];
  const store = state || {};
  const subscribe = (listen) => listeners.push(listen); 
  const dispatch = (action) => {
    storeChange(store, action);
    listeners.forEach(item => {
      item(store);
    })
  };
  return { store, dispatch, subscribe }
}
# index.js
···
const { store, dispatch, subscribe } = createStore(state, storeChange)
··· 
···
// 添加 listeners
subscribe((store) => renderApp(store));
renderApp(store);
dispatch({ type: 'BODY_TEXT', text: '我是調(diào)用 dispatch 修改的 body' });

這樣,我們每次調(diào)用 dispatch 時(shí),頁面就會(huì)重新刷新。如果我們不想刷新頁面,只想 alert 一句話,只需要更改添加的 listeners 就好了:

subscribe((store) => alert('頁面刷新了'));
renderApp(store);
dispatch({ type: 'BODY_TEXT', text: '我是調(diào)用 dispatch 修改的 body' });

這樣我們就保證了 createStore 的通用性。

四、優(yōu)化

到這里,我們似乎已經(jīng)實(shí)現(xiàn)了之前想達(dá)到的效果:我們實(shí)現(xiàn)了一個(gè)全局公用的 store , 而且這個(gè) store 的修改是經(jīng)過嚴(yán)格把控的,并且每次通過 dispatch 修改 store 后,都可以完成頁面的自動(dòng)刷新。

可是,顯然這樣并不足夠,以上的代碼仍有些簡(jiǎn)陋,存在嚴(yán)重的性能問題,

雖然我們只是修改了 body 的文案,可是,在頁面重新渲染時(shí),head 也被再次渲染。那么,我們是不是可以在頁面渲染的時(shí)候,來對(duì)比新舊兩個(gè) store 來感知哪些部分需要重新渲染,哪些部分不必再次渲染呢?

根據(jù)上面的想法,我們?cè)俅蝸硇薷奈覀兊拇a:

# storeChange.js
export const storeChange = (store, action) => {
  switch (action.type) {
    case 'HEAD_COLOR':
      return { 
        ...store,  
        head: { 
          ...store.head, 
          color: action.color 
        }
      }
    case 'BODY_TEXT':
      return { 
        ...store,
        body: {
          ...store.body,
          text: action.text
        }
      }
    default:
      return { ...store }
  }
}
# createStore.js
export const createStore = (state, storeChange) => {
  const listeners = [];
  let store = state || {};
  const subscribe = (listen) => listeners.push(listen);
  const dispatch = (action) => {
    const newStore = storeChange(store, action);
    listeners.forEach(item => {
      item(newStore, store);
    })
    store = newStore; 
  };
  return { store, dispatch, subscribe }
}
# index.js
import { state } from './redux/state.js';
import { storeChange } from './redux/storeChange.js';
import { createStore } from './redux/createStore.js';
const { store, dispatch, subscribe } = createStore(state, storeChange);
function renderHead (state){
  console.log('render head');
  const head = document.getElementById('head')
  head.innerText = state.text;
  head.style.color = state.color;
}
function renderBody (state){
  console.log('render body');
  const body = document.getElementById('body')
  body.innerText = state.text;
  body.style.color = state.color;
}
function renderApp (store, oldStore={}){
  if(store === oldStore) return; 
  store.head !== oldStore.head && renderHead(store.head);  
  store.body !== oldStore.body && renderBody(store.body);  
  console.log('render app',store, oldStore);
}
// 首次渲染
subscribe((store, oldStore) => renderApp(store, oldStore));
renderApp(store);
dispatch({ type: 'BODY_TEXT', text: '我是調(diào)用 dispatch 修改的 body' });

以上,我們修改了 storeChange ,讓他不再直接修改原來的 store,而是通過計(jì)算,返回一個(gè)新的 store 。我們又修改了 cearteStore 讓他接收 storeChange 返回的新 store ,在 dispatch 修改數(shù)據(jù)并且頁面刷新后,把新 store 賦值給之前的 store 。而在頁面刷新時(shí),我們來通過比較 newStore 和 oldStore ,感知需要重新渲染的部分,完成一些性能上的優(yōu)化。

最后

我們通過簡(jiǎn)單的代碼例子,簡(jiǎn)單了解下 redux,雖然代碼仍有些簡(jiǎn)陋,可是我們已經(jīng)實(shí)現(xiàn)了 redux 的幾個(gè)核心理念:

  • 應(yīng)用中的所有state都以一個(gè)object tree的形式存儲(chǔ)在一個(gè)單一的store中。
  • 唯一能改store的方法是觸發(fā)action,action是動(dòng)作行為的抽象。

到此這篇關(guān)于React手寫redux過程分步講解的文章就介紹到這了,更多相關(guān)React redux內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • React中useLayoutEffect鉤子使用場(chǎng)景詳解

    React中useLayoutEffect鉤子使用場(chǎng)景詳解

    這篇文章主要為大家介紹了React中useLayoutEffect鉤子使用場(chǎng)景詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • 關(guān)于react 父子組件的執(zhí)行順序

    關(guān)于react 父子組件的執(zhí)行順序

    這篇文章主要介紹了關(guān)于react 父子組件的執(zhí)行順序,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • react ant Design手動(dòng)設(shè)置表單的值操作

    react ant Design手動(dòng)設(shè)置表單的值操作

    這篇文章主要介紹了react ant Design手動(dòng)設(shè)置表單的值操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-10-10
  • 詳解前端路由實(shí)現(xiàn)與react-router使用姿勢(shì)

    詳解前端路由實(shí)現(xiàn)與react-router使用姿勢(shì)

    本篇文章主要介紹了詳解前端路由和react-router使用姿勢(shì),詳細(xì)的介紹了react-router的用法,有興趣的可以了解一下
    2017-08-08
  • Electron打包React生成桌面應(yīng)用方法詳解

    Electron打包React生成桌面應(yīng)用方法詳解

    這篇文章主要介紹了React+Electron快速創(chuàng)建并打包成桌面應(yīng)用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2022-12-12
  • React構(gòu)建組件的幾種方式及區(qū)別

    React構(gòu)建組件的幾種方式及區(qū)別

    這篇文章主要介紹了React構(gòu)建組件的幾種方式及區(qū)別,組件就是把圖形、非圖形的各種邏輯均抽象為一個(gè)統(tǒng)一的概念來實(shí)現(xiàn)開發(fā)的模式文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下
    2022-08-08
  • React中映射一個(gè)嵌套數(shù)組實(shí)現(xiàn)demo

    React中映射一個(gè)嵌套數(shù)組實(shí)現(xiàn)demo

    這篇文章主要為大家介紹了React中映射一個(gè)嵌套數(shù)組實(shí)現(xiàn)demo,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • react+antd+upload結(jié)合使用示例

    react+antd+upload結(jié)合使用示例

    這篇文章主要為大家介紹了react+antd+upload結(jié)合使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-05-05
  • React和Vue實(shí)現(xiàn)文件下載進(jìn)度條

    React和Vue實(shí)現(xiàn)文件下載進(jìn)度條

    本文主要介紹了React和Vue實(shí)現(xiàn)文件下載進(jìn)度條,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • useCallback和useMemo的正確用法詳解

    useCallback和useMemo的正確用法詳解

    這篇文章主要為大家介紹了useCallback和useMemo的正確用法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01

最新評(píng)論