[譯]遷移到新的 React Context Api

隨著 React 16.3.0 的發(fā)布,context api 也有了很大的更新。我已經(jīng)從舊版的 api 更新到了新版。這里就分享一下我(作者)的心得體會(huì)。

回顧

下面是一個(gè)展示如何使用舊版 api 的例子:

function Usage(props) {
  return (
    <Toggle onToggle={props.onToggle}>
      <Toggle.On>The button is on</Toggle.On>
      <Toggle.Off>The button is off</Toggle.Off>
      <div>
        <Toggle.Button />
      </div>
    </Toggle>
  )
}

上面的代碼會(huì)返回一個(gè)復(fù)合組件Toggle。這個(gè)組件可以讓子組件共享隱式的狀態(tài)。在某些簡(jiǎn)單的情況下可以用React.Children.map來處理。但是,這個(gè)例子需要使用 context api 來達(dá)到在 React 的某個(gè)組件樹的任意節(jié)點(diǎn)分享 state 的目的。

舊的 Context Api

這是一個(gè)舊版 context api 的應(yīng)用例子:

const TOGGLE_CONTEXT = "__toggle__";

// Toggle on
function ToggleOn({ children }, context) {
  const { on } = context[TOGGLE_CONTEXT];
  return on ? children : null;
}

ToggleOn.contextTypes = {
  [TOGGLE_CONTEXT]: PropTypes.object.isRequired
};

// Toggle off
function ToggleOff({ children }, context) {
  const { on } = context[TOGGLE_CONTEXT];
  return on ? null : children;
}

ToggleOff.contextTypes = {
  [TOGGLE_CONTEXT]: PropTypes.object.isRequired
};

// Toggle button
function ToggleButton(props, context) {
  const { on, toggle } = context[TOGGLE_CONTEXT];
  return <Switch on={on} onClick={toggle} {...props} />;
}

ToggleButton.contextTypes = {
  [TOGGLE_CONTEXT]: PropTypes.object.isRequired
};

// Toggle
class Toggle extends React.Component {
  static On = ToggleOn;
  static Off = ToggleOff;
  static Button = ToggleButton;
  static defaultProps = { onToggle: () => {} };
  static childContextTypes = {
    [TOGGLE_CONTEXT]: PropTypes.object.isRequired
  };

  state = { on: false };
  toggle = () =>
    this.setState(
      ({ on }) => ({ on: !on }),
      () => this.props.onToggle(this.state.on)
    );
  getChildContext() {
    return {
      [TOGGLE_CONTEXT]: {
        on: this.state.on,
        toggle: this.toggle,
      }
    };
  }

  render(){
    return <div>{this.props.children</div>
  }
}

在就的 API 里,你必須用一個(gè)字符串指定要分享的 state。然后通過getChildContext方法返回實(shí)際的 context。在父組件里通過childContextTypes來指定發(fā)出的 context 的類型,在子組件里用contextTypes來指定接收的 context 的類型。我(作者, 下文同)從來不喜歡這樣不直接的方式, 平時(shí)也盡量不這么做。另外,還要使用靜態(tài)屬性,才能保證 context 值傳入子組件。 我也不喜歡這樣。

另一個(gè)問題是如果shouldComponentUpdate方法返回 false 的話,context 的值是不會(huì)變的。當(dāng)然這也有變通的方法,具體可以參考這個(gè)repo

新的 Context API

新的 context api 沒有這些問題,這也是我為什么這么激動(dòng)的原因。上面的例子可以更新為:

const ToggleContext = React.createContext({
  on:false,
  toggle: () => {},
});

class Toggle extends React.Component {
  static On = ({children}) => (
    <ToggleContext.Consumer>
      {({on})=>(on ? children: null)}
    </ToggleContet.Consumer>
  )

  static Off = ({children}) => (
    <ToggleContext.Consumer>
      {({on}) => (on ? null : children)}
    </ToggleContext.Consumer>
  );

  static Button = props => (
    <ToggleContext.Consumer>
      {({on, toggle}) => (
        <Switch on={on} toggle={toggle} {...props} />
      )}
    </ToggleContext.Consumer>
  )

  toggle = () => this.setState(
    ({on}) => ({on: !on}),
    () => this.props.onToggle(this.state.on)
  );

  state = {on: false, toggle: this.toggle};

  render() {
    return (
      <ToggleContext.Provider value={}>
        {this.props.children}
      </ToggleContext.Provider>
    );
  }
}

舊的API的問題都沒有了?,F(xiàn)在不僅沒有了不直接的字符串,還有了明顯的組件:ProviderConsumer分別提供和消費(fèi)context。
每一個(gè)子組件都需要用consumer子組件,就想在就api里需要有靜態(tài)屬性一樣)。但是,兩個(gè)api的這個(gè)問題都可以通過基于render props的高階組件來解決。非常簡(jiǎn)單!

另一個(gè)通過shouldComponentUpdate返回false來更新的問題也解決了。新的context api會(huì)自動(dòng)處理這個(gè)問題。

最后一個(gè)非常好的改變是子組件都使用了render props模式。這樣在新的context api里也可以對(duì)外界暴露非常優(yōu)雅的接口。

新api的問題

這就是在新的api里通過Provider的value屬性給子組件消費(fèi)的值,只有在你想要子組件重繪的時(shí)候才會(huì)改變。
這也就是說在render方法里使用`value={{on: this.state.on, toggle: this.toggle}}是不被推薦的。
這是因?yàn)闆]次render都會(huì)傳入一個(gè)新的對(duì)象,即使state本身沒有改變。
因?yàn)槭且粋€(gè)新的對(duì)象,那么所有的子組件也都會(huì)重繪。

這個(gè)影響在實(shí)際使用的時(shí)候會(huì)很大。一般來說最好是傳入一個(gè)只有在state改變時(shí)才改變的值。也就是為什么說value={this.state}。
如果你不想傳入整個(gè)state給消費(fèi)者,那么你可以使用這個(gè)Ryan Florence的這個(gè)小技巧。

只不過這也還是會(huì)有一個(gè)小問題,我需要把toggle方法存入state里。這也顯得很奇怪,不過這只是一個(gè)小瑕疵,不影響大局。

總結(jié)

新的context api絕對(duì)是React團(tuán)隊(duì)帶來的一個(gè)非常好的改變。希望你也能和我一樣的喜歡。

另外:如果你還不能用16.3.0這個(gè)版本,那么你可以添加一個(gè)polyfill來使用新的api。這個(gè)polyfill是create-react-context

from:https://blog.kentcdodds.com/migrating-to-reacts-new-context-api-b15dc7a31ea0

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容