react中的三種方式實現祖孫資料共享

Jason秀啊發表於2020-10-17

最近在學習react的hook,看完useContext後總結一下react中的三種實現祖孫資料共享的方式。
這是一個主題換膚的例子,分別用下面三種方式來實現。先上效果圖:

在這裡插入圖片描述
下面是app.js中的程式碼:

import React from 'react';
import './App.css'

import PropsContext from './pages/props-context'
import ClassContext from './pages/class-context'
import UseContext from './pages/hook-context'

function App() {
  return (
    <div className="App">
      {/* 使用props實現資料共享 */}
      <PropsContext ></PropsContext >
      <hr/>
      {/* 使用class context實現資料共享 */}
      <ClassContext></ClassContext>
      <hr />
      {/* 使用userContext hook實現資料共享 */}
      <UseContext></UseContext>
    </div>
  );
}

export default App;

1. props-context

顧名思義使用props的方式將祖先元件的資料一層層傳遞給子元件。和vue一樣,父元件通過自定義屬性的方式將資料作為props傳給子元件,不同的是react中可以傳遞一個HTML結構(實質上是可以通過JSX轉換的js物件),render-props正是利用了這一點靈活渲染DOM實現自定義HTML結構。

import React, { Component } from 'react'
import './index.css'

const COLOR = ['#B5E61D', '#ED1C24', '#00A2E8', '#A349A4', '#B97A57', '#A349A4']

export default class grandfather extends Component {
  state = {
    theme: COLOR[0]
  }
  changeColor = () => {
    this.setState({
      theme: COLOR[Math.ceil(Math.random() * (COLOR.length - 1))]  // 隨機獲取顏色
    })
  }
  render() {
    return (
      <div className="grandfather">
        <div>當前主題為:{this.state.theme}</div>
        <div style={{ color: this.state.theme }}>grandfather</div>
        <Father theme={this.state.theme}></Father>
        <button className="fixed1" onClick={this.changeColor}>換膚</button>
      </div>
    )
  }
}

function Father(props) {
  return (
    <div className="father">
      <div style={{ color: props.theme }}>father</div>
      <Son theme={props.theme}></Son>
    </div>
  )
}

function Son(props) {
  return (
    <div className="son">
      <div style={{ color: props.theme }}>son</div>
    </div>
  )
}

2. class-context

在class類元件中使用context來將資料共享給子元件,不同於props-context,這種方式可以跨越N個父元件直接將資料傳給子元件,並不需要父->子、父->子。。。鏈式傳遞。VUE中也有使用context來實現祖孫資料共享的方案,只不過人家叫provide-inject,而react中叫provide-consumer。

import React, { Component, createContext } from 'react'
import './index.css'

const COLOR = ['#B5E61D', '#ED1C24', '#00A2E8', '#A349A4', '#B97A57', '#A349A4']
const Theme = createContext(COLOR[1])

export default class grandfather extends Component {
  state = {
    theme: COLOR[0]
  }
  changeColor = () => {
    this.setState({
      theme: COLOR[Math.ceil(Math.random() * (COLOR.length - 1))]  // 隨機獲取顏色
    })
  }
  render() {
    return (
        <div className="grandfather">
          <div>當前主題為:{this.state.theme}</div>
          <div style={{ color: this.state.theme }}>grandfather</div>
          <Theme.Provider value={this.state.theme}>
            <Father></Father>
          </Theme.Provider >
          <button className="fixed2" onClick={this.changeColor}>換膚</button>
        </div>
      
    )
  }
}

function Father(props) {
  return (
    <div className="father">
      <Theme.Consumer>
        {
          (theme) => {
            return <div style={{ color: theme }}>father</div>
          }
        }
      </Theme.Consumer>
      <Son></Son>
    </div>
  )
}

function Son(props) {
  return (
    <div className="son">
      <Theme.Consumer>
        {
          (theme) => {
            return <div style={{ color: theme }}>son</div>
          }
        }
      </Theme.Consumer>
    </div>
  )
}

3. hook-context

hook中提供了useContext這個鉤子配合createContext去實現爺孫資料共享。相當於是provide-useComtext模式。在hook中資料通過useState鉤子存放,實參為初識值,返回值為存放的資料以及相應的改變這個資料的函式。

import React, { useState ,useContext, createContext } from 'react'
import './index.css'

const COLOR = ['#B5E61D', '#ED1C24', '#00A2E8', '#A349A4', '#B97A57', '#A349A4']
const Theme = createContext('#B5E61D')

export default function Grandfather() {
  const [ theme, setTheme ] = useState(COLOR[0])
  function changeColor() {
    setTheme(COLOR[Math.ceil(Math.random() * (COLOR.length - 1))])
  }
  return (
    <div className="grandfather">
      <div>當前主題為:{theme}</div>
      <div style={{ color: theme }}>grandfather</div>
      <Theme.Provider value={theme}>
        <Father></Father>
      </Theme.Provider>
      <button className="fixed3" onClick={changeColor}>換膚</button>
    </div>
  )
}
function Father(props) {
  return (
    <div className="father">
    // 在函式元件中一樣可以使用Context.Consumer語法來拿資料
      <Theme.Consumer>
        {
          (theme) => <div style={{ color: theme }}>father</div>
        }
      </Theme.Consumer>
      <Son></Son>
    </div>
  )
}

function Son(props) {
  const theme = useContext(Theme) // 注意此處的Theme是開頭createContext('#B5E61D')的返回值Theme
  return (
    <div className="son">
      <div style={{ color: theme }}>son</div>
    </div>
  )
}

相關文章