[Expo][React Navigation]Device-Context doesn't work
atanaskanchev opened this issue · comments
Following the guide for Enabling Device-Context Prefixes, importing useDeviceContext(tw)
in the entry point of the app has no effect, i.e dark mode, breakpoints are not being intercepted. Adding it to each screen however works. Any advice?
can you double check your setup and config? lots of folks have this working for them, so i think there might be something amiss about how you've got it configured. for instance, i'm struggling to remember this off the top of my head, but i think if you use useDeviceContext
then you need to be sure you're importing your own version of tw
from a local file everywhere you're using it, instead of pulling it straight from twrnc
, similar to how you'd need to do so if you were using a custom tailwind config file.
if you still can't get it working, could you make a minimal repro i could look at? or is your project open source to where i could examine the code?
Hi @jaredh159 , I've created a minimal reproducible example with expo and react navigation. The dark mode background is not being changed on changing the device theme
import { StatusBar } from "expo-status-bar";
import tw, { useDeviceContext } from "twrnc";
import RootStackNavigator from "./navigation/root-stack-navigator";
export default function App() {
useDeviceContext(tw);
return (
<>
<RootStackNavigator />
<StatusBar style="auto" />
</>
);
}
import { Text, View, useColorScheme } from "react-native";
import tw from "twrnc";
const Home = () => {
const scheme = useColorScheme();
return (
<View
style={tw`bg-red-500 flex-1 dark:bg-blue-500 justify-center items-center`}
>
<Text style={tw`text-3xl`}>{`Current colour scheme: ${scheme}`}</Text>
</View>
);
};
export default Home;
An observation: adding useColorScheme()
and useDeviceContext(tw)
in a screen fixes the issue, but only in that particular screen. To get it working in the Other
screen, I must use useColorScheme()
Home screen: works ok
import { useNavigation } from "@react-navigation/native";
import { Text, TouchableOpacity, View, useColorScheme } from "react-native";
import tw, { useDeviceContext } from "twrnc";
const Home = () => {
const scheme = useColorScheme();
useDeviceContext(tw);
const { navigate } = useNavigation();
return (
<View
style={tw`bg-red-500 flex-1 dark:bg-blue-500 justify-center items-center`}
>
<Text style={tw`text-3xl`}>Home</Text>
<Text style={tw`text-3xl`}>{`Current colour scheme: ${scheme}`}</Text>
<TouchableOpacity
style={tw`bg-yellow-500 p-3`}
onPress={() => navigate("Other")}
>
<Text>"Go to other screen"</Text>
</TouchableOpacity>
</View>
);
};
export default Home;
Other screen: doesn't work
import { useNavigation } from "@react-navigation/native";
import { Text, TouchableOpacity, View } from "react-native";
import tw from "twrnc";
const Other = () => {
// const scheme = useColorScheme();
const { navigate } = useNavigation();
return (
<View
style={tw`bg-red-500 flex-1 dark:bg-blue-500 justify-center items-center`}
>
<Text style={tw`text-3xl`}>Other</Text>
{/* <Text style={tw`text-3xl`}>{`Current colour scheme: ${scheme}`}</Text> */}
<TouchableOpacity
style={tw`bg-yellow-500 p-3`}
onPress={() => navigate("Home")}
>
<Text>"Go to home screen"</Text>
</TouchableOpacity>
</View>
);
};
export default Other;
Ok, I've got the colour scheme changes working here, but not sure if this is the correct approach:
Move the useDeviceContext hook to the RootStackNavigator and pass the colour scheme as a key argument to the NavigationContainer
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import React from "react";
import { useColorScheme } from "react-native";
import tw, { useDeviceContext } from "twrnc";
import Home from "../screens/home";
import Other from "../screens/other";
export const RootStack = createNativeStackNavigator();
const RootStackNavigator = () => {
const scheme = useColorScheme();
useDeviceContext(tw);
return (
<NavigationContainer key={scheme}>
<RootStack.Navigator>
<RootStack.Screen name="Home" component={Home} />
<RootStack.Screen name="Other" component={Other} />
</RootStack.Navigator>
</NavigationContainer>
);
};
export default RootStackNavigator;
I suppose to have support for breakpoints we should pass the useWindowDimensions()
as a key as well
I've just realised that this seems to be the same issue as #112
yeah, i was thinking the same thing about #112. i also had one other thought this morning -- would you be able to test one thing real quick? run npm i twrnc@3.6.3
and see if the problem persists. 3.6.4
introduced a perf optimization that prevents re-renders, and i'm wondering if it's aggravating this issue. worth ruling out before we dig deeper.
Using 3.6.3
The behavour is the same.
Simulator.Screen.Recording.-.iPhone.14.Pro.Max.-.2023-08-30.at.15.57.15.mp4
Adding back the const scheme = useColorScheme();
to the screen kind of works, but the modes are mixed up
import { Text, View, useColorScheme } from "react-native";
import tw from "twrnc";
const Home = () => {
const scheme = useColorScheme();
return (
<View
style={tw`bg-red-500 flex-1 dark:bg-blue-500 justify-center items-center`}
>
<Text style={tw`text-3xl`}>{`Current colour scheme: ${scheme}`}</Text>
</View>
);
};
export default Home;
Simulator.Screen.Recording.-.iPhone.14.Pro.Max.-.2023-08-30.at.15.59.40.mp4
I created a custom hook, Which is far from ideal but it consider this as a work around
//useTWTheme.tsx
import { useColorScheme } from "react-native";
import tw, { useDeviceContext } from "twrnc";
function useTWTheme() {
const colorScheme = useColorScheme();
useDeviceContext(tw);
const isDark = colorScheme === "dark";
return { isDark, c: tw.style };
}
export default useTWTheme;
This solves a couple of things for me:
tw'pt-2 bg-blue-100'
gives me errors and therefore i want to force myself to use the tw.style prop.
const colorScheme = useColorScheme();
useDeviceContext(tw);
will make sure the darkmore classes work. Downside is that you need to import this in all components (or at least all top route components seems the. _layout.js
will not propagate properly.
const myComp = () => {
useTWTheme();
// or
const {c, isdark} = useTWTheme();
}
This only makes it easier for me not better.