React之Context


前言

React为我们提供了Context用于将数据进行广播,消费者只需使用特定的方法就能拿到广播的数据,不用再采用层层递传props的方式。如果组件之间需要共享数据,也可以采用Context的方式,如react-router中的Router组件:

...
render() {
    return (
      <RouterContext.Provider
        value={{
          history: this.props.history,
          location: this.state.location,
          match: Router.computeRootMatch(this.state.location.pathname),
          staticContext: this.props.staticContext
        }}
      >
        <HistoryContext.Provider
          children={this.props.children || null}
          value={this.props.history}
        />
      </RouterContext.Provider>
    );
  }
...

使用Provider包裹组件后,后续就能直接从context中获取最新的history等数据。

在什么情况下需要使用Context : https://react.docschina.org/docs/context.html#when-to-use-context

使用之前的考虑: https://react.docschina.org/docs/context.html#before-you-use-context

React.createContext

用于创建一个Context对象,其中包含ProviderConsumer两个组件。Provider组件可以提供用于Consumer组件消费的context。同时可以在创建Context对象时,为其指定一个默认值:

const NewContext = React.createContext(defaultValue);

如果Consumer组件不能在所处的树中匹配到Provider,将会使用这个defaultValue

如果直接给Providervalue赋值为undefined,消费组件的defaultValue不会生效

Context.Provider

Provider可以为消费组件提供context,通过将要广播的数据放入value,内部的消费组件就可以消费这个value:

const {Provider} = NewContext;
...
<Provider value='provider'>
	<Button />
</Provider>
...

Provider可以进行嵌套,但Consumer只会消费最近Provider

如果Providervalue发生变化,它内部的所有的消费组件都会重新渲染,即使使用了shouldComponentUpdate进行了优化处理,也不会影像消费组件的重新渲染。

如果为Provider提供的是对象,由于该父组件进行重渲染时,每次value都会得到一个新的对象,从而consumers组件中也会发生不必要的渲染。

解决方式是将该对象保存在state中:

const App = () => {
  const [state, setState] = useState({});
  useEffect(() => {
    ...
    setState({theme: '#fff'})
    ...
  })
  return(
  <NewContext.Provider value={state}>
  	<Button />  
  </NewContext.Provider>)
}

Class.contextType

通过给组件的contextType属性赋值为Context对象后,可以在该组件的内部拿到context:

class App extends React.Component {
  ...
  componentDidMount() {
    ...
    let value = this.context
    ...
  }
  ...
  render() {
    ...
    let value = this.context
    ...
  }
}
App.contextType = NewContext

可以在组件的任何生命周期中拿到context。也可以使用static关键字进行初始化:

class App extends React.Component {
  static contextType = NewContext
	render() {
    ...
    let value = this.context
    ...
  }
}

Context.Consumer

contextType只适用于类组件,对于函数组件,可以使用Consutmer组件获取context:

const App = () => {
  return(
  	<NewContext.Consumer>
    	{context => {
        // 拿到context,并进行其他逻辑
        
        // 返回React节点
        return <Button style={{background: context.theme}}/>
      }}
    </NewContext.Consumer>
  )
}

Consumer组件中需要一个函数来接收当前的context,函数返回一个React节点。

Context.displayName

Context对象接收一个displayName属性,类型为字符串,React DevTools 使用该字符串来确定 context 要显示的内容:

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

<MyContext.Provider> // "MyDisplayName.Provider" 在 DevTools 中
<MyContext.Consumer> // "MyDisplayName.Consumer" 在 DevTools 中

useContext

useContextReact提供的一个hooks API,可以用来拿到对应的Context对象提供的context

// context.js
export const NewContext = React.createContext(defaultValue);

// app.js
import {NewContext} from './context'
import {Buttons} from './buttons'
const App = () => {
  ...
  return(
  	<NewContext.Provider value={{theme: '#fff'}}>
    	...
      <Buttons />
    </NewContext.Provider>
  )
}

// buttons.js
import {Button} from './button'
const Buttons = () => {
  ...
  return(
    ...
  	<Button />
    ...
  )
}

// button.js
import {useContext} from 'react'
import {NewContext} from './context'
const Button = () => {
  const context = useContext(NewContext)
  
  return (
  	<button style={{background: context.theme}}>按钮</button>
  )
}

当组件上层最近的 <NewContext.Provider> 更新时,该 Hook 会触发重渲染,并使用最新传递给 NewContext provider 的 context value 值。即使祖先使用 React.memoshouldComponentUpdate,也会在组件本身使用 useContext 时重新渲染。

参考

[1] https://react.docschina.org/docs/context.html

[2] https://react.docschina.org/docs/hooks-reference.html#usecontext

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值