iT邦幫忙

2021 iThome 鐵人賽

DAY 22
0

第 22 天 !

當我們資料項下傳遞的時候,

會發現,

component 的階層越深,

傳遞資訊會越困難,

因為代表的是,需要不斷做向下傳遞的動作,

假如階層數太多,會導致我們中間傳遞過程中段的話,

很容易造成錯誤,

打個比方:

假如最後參數要使用在 Child3 , 起點在 App

<App>
  <Child1>
    <Child2>
      <Child3></Child3>
    </Child2>
  </Child1>
</App>

那我們就必須照這樣的路徑 App -> Child1 -> Child2 -> Child3

把資料送到 Child3

這樣做會產生一些問題:

  • 沒有用到的上層必須去特別為下層定義這個參數
  • 傳送鏈太長,不容易維護

那在 React 裡,有個可以打破這個規矩的存在,

就是 context !!

context 分為 Provider(提供者) & Consumer(消費者)

當使用 context 時,

會由 Provider 建立 value , 並廣播給相同 context 的 Consumer ,

那中間不用經過 props 的傳遞 ,

關係就是 Provider -> Consumer

建立 context

我們要使用 React.createContext 來建立,

import React from 'react';

export const AppContext = React.createContext({
  theme: {},
  toggleTheme: () => {},
});

createContext 本身塞入欲要提供的參數格式,非此定義的,都無法做傳送

那在我們要建立 Providercomponent 下引入我們定義好的 context,並把 Provider 放進去

like:

<AppContext.Provider value={this.state.contextParams}>
  <SafeAreaView style={styles.root}>
    <Header
      searchList={this.searchList}
      createToDoItem={this.createToDoItem}
      handleCompleteAll={this.handleCompleteAll}
    />
    <FlatList
      data={list.filter((item) => item.text.includes(filterKey))}
      renderItem={({ item, index, separators }) => {
        const isEven = index % 2 === 0;
        const isDone = item.status === 'done';
        return (
          <ToDoItem
            isEven={isEven}
            isDone={isDone}
            text={item.text}
            onPress={this.changeItemStatus(item.id)}
          />
        );
      }}
      keyExtractor={(item) => item.id}
    />
  </SafeAreaView>
</AppContext.Provider>

建立好的 context 會提供兩個 component ,

  • Provider
  • Consumer

Provider 放在資料源頭, 會提供一個 props value,

在此置入我們要提供的參數物件,

那只要 value 有變動 , 相應的 Consumer 也會產生變動,

那變動的判定方式就是用 Shallow compare (===) 去判定,

所以不能用 inline 的方式植入 value ,

否則會產生無意義的渲染,徒增消耗,

所以會放入 state 去定義,

那為什麼要放入 state ,

因為只有這樣才能去觸發 re-rendervalue 才能改變

this.state = {
  filterKey: '',
  inputValue: '',
  list: [],
  contextParams: {
    theme: {
      header: {
        backgroundColor: '#cfcf00',
      },
    },
    toggleTheme: this.toggleTheme,
  },
};

假如希望 context 裡的值,可以在 Consumer 上做改變,

那必須提供相應的改變 function,

toggleTheme = (backgroundColor) => {
  this.setState((prevState) => ({
    contextParams: {
      theme: {
        header: {
          backgroundColor,
        },
      },
    },
    ...prevState.contextParams,
  }));
};

那在指定的 component , 放入 Consumer

export class Header extends Component {
  render() {
    return (
      <AppContext.Consumer>
        {({ theme, toggleTheme }) => {
          return (
            <View style={[styles.root, theme.header]}>
              <Text style={styles.title}>My To Do List</Text>
              <View style={styles.buttonGroup}>
                <FeatureButton
                  text={'Change Color'}
                  onPress={() => toggleTheme('#F0F')}
                />
              </View>
            </View>
          );
        }}
      </AppContext.Consumer>
    );
  }
}

Consumer 的裡面放入的是 component, 不是React element

它會把 contextprops 方式傳入,

我們得到 themetoggleTheme

theme.header 定義 Header component 背景顏色,

用 toggleTheme 改變

結果如下:

Result


上一篇
Day 21 單向資料流
下一篇
Day 23 Flux
系列文
React Native & Redux 初步探討33
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言