Context

2020-05-12 17:46 更新
自 v1.3.0-beta.5 起支持 在 Taro 中沒有對 React 15 的 legacy context 進(jìn)行支持,無法使用 getChildContext() API。

在一個(gè)典型的 Taro 應(yīng)用中,數(shù)據(jù)是通過 props 屬性自上而下(由父及子)進(jìn)行傳遞的,但這種做法對于某些類型的屬性而言是極其繁瑣的(例如:地區(qū)偏好,UI 主題),這些屬性是應(yīng)用程序中許多組件都需要的。Context 提供了一種在組件之間共享此類值的方式,而不必顯式地通過組件樹的逐層傳遞 props。

API

Taro.createContext

const MyContext = Taro.createContext(defaultValue)

創(chuàng)建一個(gè) Context 對象。當(dāng) Taro 渲染一個(gè)訂閱了這個(gè) Context 對象的組件,這個(gè)組件會從最先渲染的 Provider 中讀取到 Provider 的 value。

在 Taro 中,即便在框架層面也無法知道組件的樹結(jié)構(gòu),因此 Taro 無法像 React 一樣往父組件找離自己最近的 Provider。因此創(chuàng)建的 Context 最好只在一個(gè)地方使用。

Context.Provider

<MyContext.Provider value={/* 某個(gè)值 */}>

每個(gè) Context 對象都會返回一個(gè) Provider Taro 組件,它允許消費(fèi)組件訂閱 context 的變化。

Provider 接收一個(gè) value 屬性,傳遞給消費(fèi)組件。一個(gè) Provider 可以和多個(gè)消費(fèi)組件有對應(yīng)關(guān)系。多個(gè) Provider 也可以嵌套使用,里層的會覆蓋外層的數(shù)據(jù)。

當(dāng) Provider 的 value 值發(fā)生變化時(shí),它內(nèi)部的所有消費(fèi)組件都會重新渲染。Provider 及其內(nèi)部包含 contextType 或使用 useContext 組件都不受制于 shouldComponentUpdate 函數(shù),因此當(dāng) consumer 組件在其祖先組件退出更新的情況下也能更新。

通過新舊值檢測來確定變化,使用了與 Object.is 相同的算法。

由于現(xiàn)在 Taro 還沒有 render props 的完整支持,所以無法使用 Context.Comsumer API,如果要消費(fèi) Context,可以使用 ContextType 或 useContext API。

Class.contextType

class MyClass extends Taro.Component {
  componentDidMount() {
    let value = this.context;
    /* 在組件掛載完成后,使用 MyContext 組件的值來執(zhí)行一些有副作用的操作 */
  }
  componentDidUpdate() {
    let value = this.context;
    /* ... */
  }
  componentWillUnmount() {
    let value = this.context;
    /* ... */
  }
  render() {
    let value = this.context;
    /* 基于 MyContext 組件的值進(jìn)行渲染 */
  }
}
MyClass.contextType = MyContext;

掛載在 class 上的 contextType 屬性會被重賦值為一個(gè)由 Taro.createContext() 創(chuàng)建的 Context 對象。這能讓你使用 this.context 來消費(fèi) Context 上的那個(gè)值。你可以在任何生命周期中訪問到它,包括 render 函數(shù)中。

注意: 你只通過該 API 訂閱單一 context。如果你想訂閱多個(gè),閱讀使用多個(gè) Context 章節(jié) 如果你正在使用實(shí)驗(yàn)性的 public class fields 語法,你可以使用 static 這個(gè)類屬性來初始化你的 contextType。
class MyClass extends React.Component {
  static contextType = MyContext;
  render() {
    let value = this.context;
    /* 基于這個(gè)值進(jìn)行渲染工作 */
  }
}

示例

動態(tài) Context

// counter-context.js
export const CounterContext = Taro.createContext(0);

// index.js
class Index extends Component {
  render () {
    const [ count, setCount ] = useState(0)
    return (
      <CounterContext.Provider value={count}>
        <View className='container'>
          <Test />
          <Button onClick={() => setCount(0)}>Reset</Button>
          <Button onClick={() => setCount(prevCount => prevCount + 1)}>+</Button>
          <Button onClick={() => setCount(prevCount => prevCount - 1)}>-</Button>
        </View>
      </CounterContext.Provider>
    )
  }
}

// child.js
class Child extends Taro.Component {
  shouldComponentUpdate () {
    // 即便返回 false 也不會阻止 CounterContext 更新消費(fèi)它的組件
    return false
  }

  render () {
    return <Counter />
  }
}

// counter.js
import { CounterContext } from './counter-context.js'
class Counter extends Taro.Component {
  static contextType = CounterContext

  render () {
    const value = this.context
    return (
      <View>
        Count: {value}
      </View>
    )
  }
}

我們在這個(gè)例子中把計(jì)數(shù)器 count 的值通過 CounterContext.Provider 往下傳遞,Child 組件中雖然禁止了更新,但 Counter 組件在 CounterContext.Provider 的 value 每次變化之后,還是能夠訂閱更新,收到每次 count 的值。

消費(fèi)多個(gè) Context

const ThemeContext = Taro.createContext('light');

// 用戶登錄 context
const UserContext = Taro.createContext({
  name: 'Guest',
});

class App extends Taro.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>
  );
}

// 一個(gè)組件可能會消費(fèi)多個(gè) context
function Content() {
  const theme = useContext(ThemeContext)
  const user =  useContext(UserContext)
  return (
    <ProfilePage user={user} theme={theme} />
  )
}


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號