第 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
我們要使用 React.createContext
來建立,
import React from 'react';
export const AppContext = React.createContext({
theme: {},
toggleTheme: () => {},
});
createContext
本身塞入欲要提供的參數格式,非此定義的,都無法做傳送
那在我們要建立 Provider
的 component
下引入我們定義好的 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 放在資料源頭, 會提供一個 props value,
在此置入我們要提供的參數物件,
那只要 value 有變動 , 相應的 Consumer 也會產生變動,
那變動的判定方式就是用 Shallow compare (===)
去判定,
所以不能用 inline 的方式植入 value ,
否則會產生無意義的渲染,徒增消耗,
所以會放入 state 去定義,
那為什麼要放入 state ,
因為只有這樣才能去觸發 re-render
, value
才能改變
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 ,
它會把 context
用 props
方式傳入,
我們得到 theme
& toggleTheme
,
用 theme.header
定義 Header component
背景顏色,
用 toggleTheme 改變
結果如下: