React.js |Context的作用與用法


參照react官方文檔,文章用於筆記整理。

Context是什么?

在react應用中,數據總是通過 props 自上而下進行傳遞。 這種做法對於某些類型的屬性而言是極其繁瑣的(例如:地區偏好,UI 主題)。Context 可以共享對於一個組件樹而言是“全局”的數據。這樣就不必顯式地通過組件樹的逐層傳遞 props

class App extends React.Component {
  render() {
    return <Toolbar theme="dark" />;
  }
}
//傳遞prop
function Toolbar(props) {
  return (
    <div>
      <ThemedButton theme={props.theme} />
    </div>
  );
}
//再傳遞prop
class ThemedButton extends React.Component {
  render() {
    return <Button theme={this.props.theme} />;
  }
}

使用context可以避免通過中間元素傳遞 props

//1.為 theme 創建一個 context,默認值為light
const ThemeContext = React.createContext('light');

class App extends React.Component {
  render() {
    return (
      // 2.使用 Provider 傳遞theme。在這里,將 “dark” 傳遞下去
      <ThemeContext.Provider value="dark">
        <Toolbar />
      </ThemeContext.Provider>
    );
  }
}

// !中間組件不必指明
function Toolbar() {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

class ThemedButton extends React.Component {
  // 3.指定 contextType 讀取 theme的context對象。
  static contextType = ThemeContext;
  // 4.React 會往上找到最近的 Provider,然后通過this.context讀取Provider的value值。
  render() {
    return <Button theme={this.context} />;
  }
}

Context 主要應用場景在於很多不同層級的組件需要訪問同樣一些的數據。謹慎使用,因為這會使得組件的復用性變差

API

React.createContext

創建一個Context對象:

const MyContext = React.createContext(defaultValue);

Context.Provider

Context 對象會返回的一個Provider React 組件:

<MyContext.Provider value={某個值}/>
  • Provider 接收一個 value 屬性,傳遞給消費組件(provider React 組件內的組件)
  • Provider 的 value 值發生變化時,它內部的所有消費組件都會重新渲染
  • 當消費組件沒有匹配到 Provider , Context對象的defaultValue 參數生效(注意:將 undefined 傳遞給 Provider 的 value 時,defaultValue 不會生效)
  • Provider 可以和多個消費組件有對應關系。多個 Provider 也可以嵌套使用,里層的會覆蓋外層的數據

Class.contextType

如果使用public class fields 語法,可以用 static 初始化contextType。contextType指定了需要讀取的Context對象

class MyClass extends React.Component {
  static contextType = MyContext;
  render() {
    let value = this.context;
  }
}

更常用的是Class.contextTypeClass.contextType可以對Context對象重賦值:

class MyClass extends React.Component {
 //...
  render() {
    let value = this.context;
  }
}
//重賦值
MyClass.contextType = MyContext;

掛載在 class 的 contextType 會被重賦值為一個由 React.createContext() 創建的 Context 對象。使你用 this.context 來訪問最近 Context 值。可以在任何生命周期中訪問到它

Context.Consumer

基於 context 值渲染React節點

<MyContext.Consumer>
  {value => /* 基於 context 值進行渲染*/}
</MyContext.Consumer>

這需要函數作為子元素(function as a child)這種做法。函數接收 context 值,返回一個 React 節點。傳遞給函數的 value 值等於離這個 context 最近的 Provider 提供的 value 值。

Context.displayName

在React DevTools 中對顯示的context對象的名稱進行更改

const MyContext = React.createContext(/* some value */);
MyContext.displayName = 'MyDisplayName';

<MyContext.Provider> // 在 DevTools 中會顯示"MyDisplayName.Provider" 

示例01-動態Context

theme-context.js:儲存themes對象和theme的context對象

//1. 聲明一個themes對象儲存主題 導出
export const themes = {
    light: {
      background: 'orange',
    },
    dark: {
      background: '#eee',
    },
  };
  
//2. 創建context對象 導出
export const ThemeContext = React.createContext(
    themes.dark     //2.設置默認值dark
);

themed-button.js:創建一個加了主題的按鈕組件(ThemedButton)

//1. 引入theme的context對象
import {ThemeContext} from './theme-context';

class ThemedButton extends Component {
  render() {
    let props = this.props;
    return (
      <button
        {...props}
        style={{backgroundColor: this.context.background}} //3. 讀取值
      />
    );
  }
}

//2. context重賦值
ThemedButton.contextType = ThemeContext;

//4. 導出按鈕組件
export default ThemedButton;

App.js

//1.引入context對象和themes對象
import {ThemeContext, themes} from './theme-context';
//2.引入按鈕組件
import ThemedButton from './themed-button';

// 中間組件 放入按鈕,用於切換主題
function Toolbar(props) {
  return (
    <ThemedButton onClick={props.changeTheme}>
      Change Theme
    </ThemedButton>
  );
}

//父組件
class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      theme: themes.light,
    };

    //3.定義切換主題的事件
    this.toggleTheme = () => {
      this.setState(state => ({
        theme:state.theme === themes.dark? themes.light:themes.dark,
      }));
    };
  }

  render() {
    return (
      <div>
        {/* 4.給Context對象的Provider的value賦值*/}
        <ThemeContext.Provider value={this.state.theme}>
          {/* 5.prop賦值 傳入事件*/}
          <Toolbar changeTheme={this.toggleTheme} />
        </ThemeContext.Provider>

        {/* 6.用於對比,由於沒有Provider提供value值,所以會讀取context對象的默認值為dark */}
        <div>
          <Toolbar />
        </div>
      </div>
    );
  }
}
export default App

示例02-在嵌套組件中更新 Context

通過 context 傳遞一個函數,使得消費組件自行更新 context(場景:在嵌套很深的組件中更新 context ):

theme-context.js

export const ThemeContext = React.createContext({
  theme: themes.dark,
  toggleTheme: () => {},
});

theme-toggler-button.js

import {ThemeContext} from './theme-context';

// 這個按鈕組件不僅獲取了 theme 值,也從 context 中獲取到一個 toggleTheme 函數
function ThemeTogglerButton() {
  return (
    //Context.Consumer:基於 context 值渲染React節點
    <ThemeContext.Consumer>
      {({theme, toggleTheme}) => (
        <button onClick={toggleTheme}
          style={{backgroundColor: theme.background}}>
          Toggle Theme
        </button>
      )}
    </ThemeContext.Consumer>
  );
}
export default ThemeTogglerButton;

App.js

import {ThemeContext, themes} from './theme-context';
import ThemeTogglerButton from './theme-toggler-button';

class App extends Component {
  constructor(props) {
    super(props);
    //1.定義事件
    this.toggleTheme = () => {
      this.setState(state => ({
        theme:state.theme === themes.dark? themes.light:themes.dark,
      }));
    };
    //2。定義state
    this.state = {
        theme: themes.light,
        toggleTheme: this.toggleTheme,
      };
  }
  render() {
    // 3.給Provider的value賦值,包含了當前的主題和事件
    return (
      <ThemeContext.Provider value={this.state}>
        <ThemeTogglerButton />
      </ThemeContext.Provider>
    );
  }
}

示例03-消費多個 Context

為了確保 context 快速進行重渲染,React 需要使每一個 消費組件的 context 在組件樹中成為一個單獨的節點。

App.js

import ProfilePage from './ProfilePage'

// 1.創建主題context
const ThemeContext = React.createContext({
  bgColor:'#eee',
  fontColor:'balck'
});
// 2.創建用戶context
const UserContext = React.createContext({
  name: 'Guest',
});


class App extends Component {
  constructor(props){
    super(props)
    //3.設置在app組件的想要的狀態
    this.state = {
      theme: {
        bgColor:'orange',
        fontColor:'white'
      },
      user: {
        name:'Jack'
      },
    };
  }
    
  render() {
    const {theme,user}=this.state
    //4.給Provider的value傳入值
    return (
      <ThemeContext.Provider value={theme}>
        <UserContext.Provider value={user}>
            <Content />
        </UserContext.Provider>
      </ThemeContext.Provider>
    );
  }
}

function Content() {
  //5.現在可以訪問到Provider傳過來的theme和user
  return (
    <ThemeContext.Consumer>
      {theme => (
        <UserContext.Consumer>
          {user => (
            <ProfilePage user={user} theme={theme} />
          )}
        </UserContext.Consumer>
      )}
    </ThemeContext.Consumer>
  );
}

export default App

ProfilePage.js

export default function ProfilePage (props){
    const {user,theme}=props
    return(
        //給style傳入了一個對象
        <div style={{display:'inline-block',padding:'5px 8px',backgroundColor:theme.bgColor,color:theme.fontColor}}>
            {user.name}
        </div>
    )
}

可在分支10、11、12中獲取示例源碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM