zoukankan      html  css  js  c++  java
  • 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中获取示例源码

  • 相关阅读:
    jQuery 核心
    Visual Studio 文件没发布出来
    冷门JS技巧
    项目发布: error CS0103: 当前上下文中不存在名称“*****”
    jQuery编程的最佳实践
    HTML5中类jQuery选择器querySelector的使用
    html dl dt dd标签元素语法结构与使用
    EF Code First 更新数据库, 数据库迁移
    ASP.NET MVC中的拦截器
    C#Linq中的Union All/Union/Intersect和Top/Bottom和Paging和SqlMethods,skip,take,takewhile,skipwhile,编译查询等
  • 原文地址:https://www.cnblogs.com/sanhuamao/p/13726574.html
Copyright © 2011-2022 走看看