Closed Bryant-Anjos closed 2 years ago
where is the darkTheme
variable coming from? is it controlled by useState
? Or some other mechanism?
One thing to be careful of is that React.useMemo()
can prevent re-renders when the colorScheme changes high up in the component hierarchy. Are you using useMemo()
or React.PureComponent
anywhere?
I have found the problem to be that if you're not checking for dark mode within the screen/component it is not going to re-render after dark mode changes. If you reload the app with dark mode active the styles work.
Try adding this to a component/screen you're testing and see if it changes then...
import { useColorScheme } from 'react-native'
// ...
const isDarkMode = useColorScheme()
@jaredh159 Is every component supposed to re-render after dark mode changes? If not; do we need to manually pull in useColorScheme everywhere to make it work? I'm just using darkMode: 'media'
in tailwind.config.js
btw, not changing via setColorScheme
; and I've done this also.
Every component does not need to pull in useColorScheme. Most users shouldn't ever need to even pull in that function from react native, instead you should be able to use the useDeviceContext
once at the root of your component heirarchy, and when dark mode changes, all of the components should re-render, provided they're not memoized with useMemo
or React.PureComponent
, or have some sort of custom componentShouldUpdate
logic. Be sure to check the readme for how to configure with useDeviceContext()
.
Now, if you're using RN for web, I can't really speak much to that. This library does not aim to support RN web, although I know people are using it in that context.
Ok that's what I thought. It does work directly in my App.tsx
however for any code outside it does not (I have useDeviceContext
setup and nothing is memoized or using PureComponent). Is there anything extra required for React Navigation? I'm not sure how to debug further from this. Here's my super basic App.tsx
:
import { NavigationContainer } from '@react-navigation/native'
import tw from 'lib/tailwind'
import AppTabNavigator from 'navigators/AppTab'
import { SafeAreaProvider } from 'react-native-safe-area-context'
import { useDeviceContext } from 'twrnc'
export default function App() {
useDeviceContext(tw)
return (
<SafeAreaProvider>
<NavigationContainer>
<AppTabNavigator />
</NavigationContainer>
</SafeAreaProvider>
)
}
And my lib/tailwind.ts
:
import { create } from 'twrnc'
const tw = create(require('../../tailwind.config.js'))
export default tw
where is the
darkTheme
variable coming from? is it controlled byuseState
? Or some other mechanism?
I'm using redux.
One thing to be careful of is that
React.useMemo()
can prevent re-renders when the colorScheme changes high up in the component hierarchy. Are you usinguseMemo()
orReact.PureComponent
anywhere?
I don't. None of These.
In my project I'm using the react native paper, and I noticed when using the useTheme() in the same component the theme changes correctly.
I'm a bit stumped then. I think I'll try to do some digging within the next day or two and see if I can replicate.
@Bryant-Anjos I did a little digging today, and I'm having trouble replicating. Any chance your project is open source, where I could clone it and troubleshoot? Right now my best guess is that one of your dependencies is usine React.memo
or useMemo
somewhere in your component hierarchy, which is preventing the re-renders when the dark-mode changes.
@Bryant-Anjos I did a little digging today, and I'm having trouble replicating. Any chance your project is open source, where I could clone it and troubleshoot? Right now my best guess is that one of your dependencies is usine
React.memo
oruseMemo
somewhere in your component hierarchy, which is preventing the re-renders when the dark-mode changes.
@jaredh159 In my case I'm not using memo or useMemo but it seem not working the first button click and second will work. here is my repo
@Bryant-Anjos I did a little digging today, and I'm having trouble replicating. Any chance your project is open source, where I could clone it and troubleshoot? Right now my best guess is that one of your dependencies is usine
React.memo
oruseMemo
somewhere in your component hierarchy, which is preventing the re-renders when the dark-mode changes.
@jaredh159 it's a private project, I cannot open source it. But I'll try reproduce the problem in an open source app later.
I think I'm having the same problem.
I'm trying to use the dark:
prefix, and it works, but I have to close the component and re-open it for a visual change. The component doesn't refresh by itself (if it's already open).
I'm using tw.setColorScheme('dark');
(hard-coding it to 'dark'
just for debugging purposes).
If you any more information, let me know. I'm happy to provide it.
If someone would be up for creating a very minimal reproduction open source repository, I'd be grateful, and I'd definitely dig in and try to find the root of the problem. Ideally just a plain expo RN app, with no other dependencies other than twrnc
. I have an app like that locally that I test with, and I can't get the problem to manifest.
I appreciate that @rimsila linked to an open source project, but it's got lots of dependencies, and I wouldn't consider it a minimal reproduction.
That said, it's very possible there is some core bug or logic/render edge case here, since several of you are experiencing this. None of my RN apps actually use dark mode, so maybe that's why I haven't run into it. But, my apps do use the faux media-queries, which work pretty much exactly the same -- deeply nested components need to rerender when some device attribute changes (like switching from portrait to landscape) -- and my experience has been that it works great, as long as I don't have a memoized component in the tree preventing re-renders down the line. That's why I keep wondering if these problems might be due to third-party libraries implementing memoization somewhere.
If it turns out that memoization is in fact the culprit, it might be the case that enough common third party deps use that technique, that maybe this library needs to have some sort of alternate method of ensuring re-renders. I'm willing to consider that and work on something to that end, but first I need to figure out exactly what's going on, and could definitely use some help with that step.
I'll try and set up something from scratch tonight.
@jaredh159 I set up a barebones React Native project and installed this library. Check it out over here.
I have the feeling that either I'm massively overlooking something, because tw.setColorScheme()
does nothing for me. However when I manually toggle iOS' dark mode (in "settings", then "developer", then "dark appearance") it works like a charm.
I would appreciate it if you could have a look and tell me if I'm doing something wrong, cheers 🙌
@range-of-motion thanks for the reproduction. I see the problem now. When you manually use tw.setColorScheme()
you're informing tw
that the color scheme changed, but it doesn't automatically cause react to re-render. You'd need to (currently) wrap up that function in something that also sets state. This is a bit ugly, but works:
const [mode, setMode] = useState('light');
// [...] then, in your component somewhere...
<TouchableOpacity
onPress={() => {
tw.setColorScheme('dark');
setMode('dark'); // 👋 <-- this line will trigger the re-render
}
/>
Admittedly, this feels like a bit of a kludge. I think maybe what I should do is create another custom hook, something like useManualColorScheme()
that bakes in the state, forcing the re-render. That would be a good bit more ergonomic.
ok, 2.2.0
now has a useAppColorScheme()
hook to make this easier. See docs for how to integrate:
https://github.com/jaredh159/tailwind-react-native-classnames#taking-control-of-dark-mode
Thanks for bringing this to my attention!
@jaredh159 Hi, didn't want to open a new issue because it's a kinda suggestion then issue. So in the documentation, you added next
const [colorScheme, toggleColorScheme, setColorScheme] = useAppColorScheme(tw);
Let's I need just setColorScheme And I cant do next
const [ setColorScheme] = useAppColorScheme(tw);
And need to do next
const [ , ,setColorScheme] = useAppColorScheme(tw);
Is it possible in the feature to do next ?
const {setColorScheme, ...etc} = useAppColorScheme(tw);
Or I'm missing something? Thank you!
@fristyr you would need to do [, , setColorScheme] = ...
as you pointed out. I don't think I'll change it to an object, because it's more consistent with React's useState()
api, and I don't think a couple commas is too big of a deal.
@jaredh159 No no, it's not a big deal for sure. Good that I did not open a new issue for this simple question. Anyways thanks! Working properly
@fristyr sure thing, thanks for the feedback. if it turns out that most people just want access to the setColorScheme
function, maybe i'll consider re-ordering for better ergonomics, or moving to an object for destructuring.
Hi! I'm trying to change the app theme when the user changes it inside the app. But it's not working. The code example is below. Is there something wrong?