问题描述
我正在尝试在我的 React-Native Expo 应用程序(在 android 上)中实现一个主题,该主题通过切换开关进行更改。尽管互联网上有大量指南,但大多数指南都不完整或过于简单(扩展性很差)。无论如何,我几乎可以工作了,下面的代码不是原始问题,而是最小的问题。
我想我的 ThemeContext.js 很好,因为在一种情况下(下面的 App1)主题确实改变了。
ThemeContext.js:
import React from 'react';
export const themes = {
light: {
foreground: '#000000',background: '#eeeeee',special: 'salmon',},dark: {
foreground: '#ffffff',background: '#222222',special: 'violet',};
const ThemeContext = React.createContext({theme: themes.light});
function ThemeProvider(props){
console.log(`theme context provider component called`)
const [theme,setTheme] = React.useState(themes.light);
const toggleTheme = () => {
console.log(`toggle called`);
setTheme(
theme === themes.dark
? themes.light
: themes.dark);
}
return (
<ThemeContext.Provider value={{theme,toggleTheme}}>
{props.children}
</ThemeContext.Provider>
);
}
export default ThemeContext;
export {ThemeProvider};
以下是应用程序本身:
App.js:
import React,{useContext,useState } from "react";
import {StyleSheet,Switch,Text,View,SafeAreaView } from "react-native";
import ThemeContext,{ThemeProvider} from './ThemeContext';
function ThemetoggleSwitch({textColor}){
const {toggleTheme} = useContext(ThemeContext);
const [isEnabled,setIsEnabled] = useState(false);
const toggleSwitch = () => {
setIsEnabled(prevIoUsstate => !prevIoUsstate);
toggleTheme();
}
return (
<View style={{flex: 1,flexDirection:'row',alignItems: "center",paddingLeft:20,paddingTop:20}}>
<View>
<Text style={{color: textColor,fontSize:12}}>Dark Theme:</Text>
</View>
<Switch
trackColor={{ false: "#767577",true: "#81b0ff" }}
thumbColor={isEnabled ? "#f5dd4b" : "#f4f3f4"}
ios_backgroundColor="#3e3e3e"
onValueChange={toggleSwitch}
value={isEnabled}
/>
</View>
);
}
function TextViewWithTheme(){
const {theme} = useContext(ThemeContext);
return (
<View style={{backgroundColor: theme.background}}>
<Text style={{fontSize:17,color:theme.foreground}}>
Did I change color?
</Text>
</View>
);
}
function ThemeTestApp1(){
const {theme} = useContext(ThemeContext);
return (
<SafeAreaView style={{...styles.container,backgroundColor:theme.special}}>
<ThemetoggleSwitch />
<TextViewWithTheme/>
<View style={{backgroundColor: theme.background}}>
<Text style={{fontSize:17,color:theme.foreground}}>
Did I change color?
</Text>
</View>
</SafeAreaView>
);
}
function App1(){
return (
<ThemeProvider>
<ThemeTestApp1/>
</ThemeProvider>
);
}
function App2() {
const {theme} = useContext(ThemeContext);
return (
<ThemeProvider>
<SafeAreaView style={{...styles.container,color:theme.foreground}}>
Did I change color?
</Text>
</View>
</SafeAreaView>
</ThemeProvider>
);
}
//change here between App1 and App2 to see the difference
export default function App(){
return <App1/>;
}
const styles = StyleSheet.create({
container: {
flex: 1,});
在 App 组件中,您可以轻松地在 App1 和 App2 之间切换并查看差异。
在 App1 中,我在 ThemeProvider 中调用 ThemeTestApp1。这里一切正常:切换开关时所有颜色都会改变(好吧,除了第一次加载应用程序,背景都是白色/未定义,但这可能是一个不同的问题)。
另一方面,在 App2 中,我在 App2 的函数体中使用了 ThemeContext,并在 ThemeProvider 中使用了 ThemeTestApp1 的 return 语句代码。据称,App1 和 App2 应该以相同的方式工作,但事实并非如此。出于某种原因,即使 App1 工作正常,App2 也不会在渲染前“加载”主题(这是我的猜测)。
所以问题是:如何让 App2 正常工作而不创建更多不必要的组件?
如果这里的规则是“使用最接近使用它的组件的 useContext”,那么这并不能解释为什么 App1 中 ThemeTestApp1 中的 View 会改变颜色,而在 App2 中却不会?在这两种情况下,它都不是最深的组件,也不是根。
“作为提供者后代的所有消费者都将重新渲染 每当 Provider 的 value 属性发生变化时。传播自 提供给其后代消费者(包括 .contextType 和 useContext) 不受 shouldComponentUpdate 方法的影响,因此 即使祖先组件跳过更新,消费者也会更新。”
所以,据我所知,这应该不是问题。
如果你认为这里的上下文是一个对象而不是一个原始对象(如上面链接中的警告部分所述),那么他们会说它会渲染太多而不是太少。
链接到 a snack I made(虽然它与在 devtools/metro-bundler 上运行 expo 产生的结果略有不同,但要点仍然存在)
谢谢
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)