jaredh159 / tailwind-react-native-classnames

simple, expressive API for tailwindcss + react-native

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[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;

Simulator Screenshot - iPhone 14 Pro Max - 2023-08-30 at 11 36 23

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.

v4.0.0 (beta available now with npm install twrnc@next) exposes a memoization buster (tw.memoBuster) that can be passed as a key to break memoization.

see #271. when that is merged, i'm going to close this. if memoization issues are still found, let's consolidate discussion at #112.