React Context 基本使用

前言

Context 通过组件树提供了一个传递数据的方法(类似一个简易的redux来存放公共数据),从而避免了在每一个层级手动的传递 props 属性。
有部分小伙伴应该使用props属性进行组件向下传值的操作。当多个组件嵌套时候,你需要从最外层的组件一层一层地通过props将数据传到最里层的组件时,你就需要慢慢向上寻找最初的值是什么,何苦呢?

Context 提供了一种在组件之间共享这些值的方法,而无需显式地通过树的每一层传递 prop。

1. 基本使用

(1)创建和注入context

context一般在顶层组件创建,方便数据的全局注入和全局共享

import React,{Component,createContext} from 'react'

//使用createContext创建context的初始默认值
//可以是对象,字符串等任意类型的值
export const GlobalContext = createContext({name:'scw'})
//App为顶层组件
class App extends Component{
    constructor(props) {
        super(props);
    }
    
    handleClick = (e) => {
        console.log('组件B点击了');
    }
    render() {
        return (
        //使用Provider进行context全局注入
        //这里的value表示对context重新赋值
            <GlobalContext.Provider value={{name:'scw1',onClick:this.handleClick.bind(this)}}>
                <B />
            </GlobalContext.Provider>
        )
    }
}
export default App;

(2)消费Context

import { GlobalContext } from './App'
const B = class extends Component{
    render() {
        return <GlobalContext.Consumer>
            {(globalContext)=>
            	//消费Context的数据
                <span onClick={globalContext.onClick}>
                {globalContext.name}

                </span>
            }
        </GlobalContext.Consumer>
    }
}
export default B
  • 使用Provider在顶层组件注入后,里层的所有子组件及其后代组件均可访问到对应的context数据
  • 调用creatContext时可以传入默认值,但该值不是Provider的默认值,而是作为Consumer的默认值,仅当没有Provider的时候才生效。换句话说,只有在没有使用Provider的情况下使用了Consumer,Consumer消费的才是默认值。如果注入Provider时没有设置value值,则Consumer拿到的value为undefined
  • 多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据。
  • 当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染。Provider 及其内部 consumer 组件都不受制于 shouldComponentUpdate 函数,因此当 consumer 组件在其祖先组件退出更新的情况下也能更新。
// Theme context,默认的 theme 是 “light” 值
const ThemeContext = React.createContext('light');

// 用户登录 context
const UserContext = React.createContext({
  name: 'Guest',
});

class App extends React.Component {
  render() {
    const {signedInUser, theme} = this.props;

    // 提供初始 context 值的 App 组件
    return (
      <ThemeContext.Provider value={theme}>
        <UserContext.Provider value={signedInUser}>
          <Layout />
        </UserContext.Provider>
      </ThemeContext.Provider>
    );
  }
}

function Layout() {
  return (
    <div>
      <Sidebar />
      <Content />
    </div>
  );
}

// 一个组件可能会消费多个 context
function Content() {
  return (
    <ThemeContext.Consumer>
      {theme => (
        <UserContext.Consumer>
          {user => (
            <ProfilePage user={user} theme={theme} />
          )}
        </UserContext.Consumer>
      )}
    </ThemeContext.Consumer>
  );
}

2. contextType

给顶层class组件(即Provider注入的那个组件中)设置静态属性contextType并赋值为新建的context,则你可以在任何生命周期中通过this.context访问到它,包括 render 函数中。

const MyContext = React.createContext(defaultValue);
class MyClass extends React.Component {
	static contextType = MyContext 
  componentDidMount() {
    let value = this.context;
    /* 在组件挂载完成后,使用 MyContext 组件的值来执行一些有副作用的操作 */
  }
  componentDidUpdate() {
    let value = this.context;
    /* ... */
  }
  componentWillUnmount() {
    let value = this.context;
    /* ... */
  }
  render() {
    let value = this.context;
    /* 基于 MyContext 组件的值进行渲染 */
  }
}

如果是函数组件,则在props后面,会多出一个参数context:

const Title = (props, context) => {
  const {textColor} = context.theme;
  return (
    <p style={{color: color}}>
      我是标题
    </p>
  );
};

Title.contextTypes = {
  theme: PropTypes.object
};


3. 注意事项

因为 context 会使用参考标识(reference identity)来决定何时进行渲染,这里可能会有一些陷阱,当 provider 的父组件进行重渲染时,可能会在 consumers 组件中触发意外的渲染。举个例子,当每一次 Provider 重渲染时,以下的代码会重渲染所有下面的 consumers 组件,因为 value 属性总是被赋值为新的对象:

class App extends React.Component {
  render() {
    return (
      <MyContext.Provider value={{something: 'something'}}>
        <Toolbar />
      </MyContext.Provider>
    );
  }
}

为了防止这种情况,将 value 状态提升到父节点的 state 里:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: {something: 'something'},
    };
  }

  render() {
    return (
      <Provider value={this.state.value}>
        <Toolbar />
      </Provider>
    );
  }
}

所以,一般使用context都是在Provider组件中用state来控制全局状态,然后在Consumer组件中通过调用context中定义的函数state来达到最终回到Provider来更新state达到更新context数据的效果

我们并不建议大量使用 context,因为尽管它可以减少逐层传递,但当组件结构复杂的时候,我们并不知道 context 是从哪里传过来的。Context 就像一个全局变量一样,而全局变量正是导致应用走向混乱的罪魁祸首之一,给组件带来了外部依赖的副作用。在大部分情况下,我们并不推荐使用 context 。使用 context 比较好的场景是真正意义上的全局信息且不会更改,例如界面主题、用户信息等

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值