How to wrapper createStackNavigator with redux? - react-native

I think my question is clear. Before I start, I should show about the project structure I created myself.
I added the router logic to the project later and I think it will work better.
stack_main.js following;
import Screen from '../screens';
import {createStackNavigator} from 'react-navigation-stack';
export default createStackNavigator(
{
HomeScreen: Screen.HomeScreen,
MyEarningsScreen: Screen.MyEarningsScreen,
// TestScreen: Screen.TestScreen,
},
{
cardStyle: {
backgroundColor: '#000000',
},
},
);
In fact, if you can change the value of "cardStyle.backgroundColor" in any way, there is no need to wrap it with redux. Since redux is state management, I can't wrap the createStackNavigator function in the react-navigation module or I don't know.
I need to change the "cardStyle.backgroundColor" value every time a stack is pushed. How can I do it ?
Thank you for interest.
Why do I want this?
I created a side menu using react-native-drawer as in the image below.
In the module I used, no matter what I gave the offset props value, there were white parts of the background color; As a result of my research as a solution to this cardStyle I saw and applied.

Update to the latest version of react-navigation-stack and then use screenProps customize the colors dynamically.

Related

React Native - What's the best way to provide a global theme?

I've started using RN recently and have been doing some research on the implementation of themes and/or a dark-light mode. Here's roughly how I understand the two main options so far:
Context: Easy setup and can be accessed via hook inside the component that needs it. I count things like the React Navigation themes since that works, in essence, the same way(?)
Styled Components: Essentially just replacing the components with custom ones that access the current theme and that can then be set up to change their props on toggle if needed.
What I don't like about context, is that (the way I understand it) it wouldn't let me access the theme when using a StyleSheet, since that's outside the component. With the styled components on the other hands I'm not sure if I can cover all options inside my custom components, wouldn't I still need some sort of hook in each component anyway?
I was also thinking about just saving the current theme into my Store (in my case Zustand), together with an action that lets me toggle to a dark-mode. But so far, I haven't really seen anyone else do that, is there a downside to doing it that way?
It's not hard to pass context to your stylesheet, it just requires a bit of extra boilerplate. Something like the below:
import ThemeContext from '<path>';
export default () => {
const theme = useContext(ThemeContext);
const stylesWithTheme = styles(theme);
return <Text style={stylesWithTheme.text>Hi</Text>;
}
const styles = theme => StyleSheet.create({
text: {
color: themeStyles.color[theme];
}
});
const themeStyles = {
color: {
dark: '#FFF',
light: '#000',
},
};
I don't think using styled components would be a big departure from the above scheme.
If you did store your theme in state, and define your styles within the body of the component, that could be a savings in boilerplate. It's up to you whether having your styles in the component body is acceptable.
Side note, if you're already using a state manager, I would recommend not mixing it with the Context API without knowing more. It's simpler to have one solution there.

How to unmount screen on blur in a StackNavigator?

Im using React Navigation x5, I have StackNavigator that's loading bunch of screens. I want to unmount one particular screen component on blur to get the same result of using unmountOnBlur when using a DrawerNavigator.
How can I achieve that?
There are currently three ways to do it since 5.x. You can either
use useFocusEffect hook to trigger an action (recommended), or
use useIsFocused hook, or
listen to the focus event
Example of useFocusEffect
import { useFocusEffect } from '#react-navigation/native';
function Sample(props) {
const [shouldHide, setShouldHide] = React.useState(false);
useFocusEffect(() => {
setShouldHide(false)
return () => {
setShouldHide(true)
}
});
return shouldHide ? null : <InnerComponent {...props} />;
}
Example of useIsFocused hook
import * as React from 'react';
import { Text } from 'react-native';
import { useIsFocused } from '#react-navigation/native';
function Profile() {
// This hook returns `true` if the screen is focused, `false` otherwise
const isFocused = useIsFocused();
return {isFocused ? <Text>Inner component</Text> : null};
}
Note:
Using this hook component may introduce unnecessary component re-renders as a screen comes in and out of focus. ...
Hence we recommend to use this hook only if you need to trigger a re-render.
More information can be found in the documentation Call a function when focused screen changes
Besides that, stack navigators come with a prop called detachInactiveScreens which is enabled by default. Link to the documentation
detachInactiveScreens
Boolean used to indicate whether inactive screens should be detached from the view hierarchy to save memory. Make sure to call enableScreens from react-native-screens to make it work. Defaults to true.
(The instruction on enabling React Native Screens can be found at https://reactnavigation.org/docs/react-native-screens/#setup-when-you-are-using-expo)
Inside the stack navigator's options object, there is also a detachPreviousScreen prop to customise the behaviour of certain screens. This option is usually enabled as well.
detachPreviousScreen
Boolean used to indicate whether to detach the previous screen from the view hierarchy to save memory. Set it to false if you need the previous screen to be seen through the active screen. Only applicable if detachInactiveScreens isn't set to false. Defaults to false for the last screen when mode='modal', otherwise true.

Send props between Components (no related) React-Native

I have Components directory, there is js file Timer where I have countdown Component
const [countdownTimer, setCountdownTimer] = useState(15);
This component only returns <Text>{countdownTimer}</Text> on screen.
I also have another component Description. I want to handle this state in my Description component to make some changes after time has changed. The components aren't related (they aren't imported in each other)
I tried to import Description in Timer to send value as a props like
<Description setCountdownTimer={setCountdownTimer} />
and with style hide it but display: none isn't working on Android. I also try to just hide with another method
{false && <Description setCountdownTimer={setCountdownTimer} />}
but it's also not working, I got "undefined" in console.
For your case you should take a look at React Context: https://reactjs.org/docs/context.html
Hi #Danny If I understood your question correctly, you are trying to share the countDown state between two different components, and the way you try to do it may complicate you a bit, if not I suggest you use redux which is a library designed in order to manage the global states of applications, with redux you will be able to share the states of your application with several components.
You should need to create reducers and thanks to the createStore method from `redux you will be able to use a global state to your entire application
Here it's an example of code you can improve depending on your need :
import { createStore } from 'redux';
import reducer from './reducer';
import { Provider } from 'react-redux';
const store = createStore(reducer);
export default function App () {
return (
<Provider store = {store}>
<YourMainComponent />
</Provider>
)
}
You just need to wrapp your main component with the Provider component from 'react-redux' to access to your global state and manage it as you want. See more here
https://react-redux.js.org/introduction/basic-tutorial

ReactNative scrollToEnd() on FlatList - Functional Component

Am trying to implement a chat app using ReactNative. The problem am facing is, after new item is added to the FlatList, am trying to scroll the FlatList items to the bottom so that the newly added message is visible.
When I checked the documentation, I can see a method called scrollToEnd(). But not sure how to use it as am following the Functional Component style of coding. Because when I googled, I can see examples where it uses the ref in a Class Component style coding.
But couldn't find how to use it in Functional Component!
I think you're looking for useRef
// I hope you don't mind the typescript
import {useRef} from 'react';
import {FlatList} from 'react-native';
export const Comp = () => {
const flatListRef = useRef<FlatList<any>>();
// however you detect new items
flatListRef?.current?.scrollToEnd();
return (
<FlatList
data={[]}
renderItem={() => null}
ref={flatListRef}
/>
);
}
But I think if you use inverted={true} on the flat list, it should snap to the top, or better bottom (I think).
For typescript you can use just:
const listRef = useRef<FlatList>(null);
and in your code you just need to check if your ref is exist:
listRef?.current?.scrollToIndex({animated: true, index: yourIndex})

How to create different top tabs in multiple screens in React Native

I am looking to create different tabs in different screens. This is a little hard to explain so i will post a couple photos to illustrate my desire output.
I've already created a tab navigator using createMaterialTopTabNavigator, but it seems like i can't apply the same logic twice in a whole separate js file. My javascript is fairly weak.
This is my code for the first tab navigation(newsfeed + services). I am looking to do the exact same thing except with different tab titles.
My question is, how would i go about achieving my desire output?
import {createMaterialTopTabNavigator} from 'react-navigation';
import NewsfeedActivity from './NewsfeedActivity';
import ServiceActivity from './ServiceActivity';
export default createMaterialTopTabNavigator({
Newsfeed:{screen: NewsfeedActivity},
Services:{screen:ServiceActivity}
},
{
initialRouteName:'Services',
swipeEnabled:true,
navigationOptions:({navigation})=>({
header:null
}),
tabBarOptions:{
activeTintColor:'#65FAE9',
inactiveTintColor:'white',
allowFontScaling:true,
indicatorStyle:{borderBottomColor:'#65FAE9', borderBottomWidth:4,},
style:{backgroundColor:'#515276',paddingBottom:5},
labelStyle:{fontWeight:'bold',marginTop:'40%'},
},
},
);
What i have:
What Im looking to create:
You can nest multiple navigators.
If your desired output is to have the bottom navigation different in "Newsfeed" and different in "Services" , then instead of passing a page as the screen , you can pass a bottomNavigator
import {createMaterialTopTabNavigator,createBottomTabNavigator} from 'react-navigation';
import NewsfeedActivity from './NewsfeedActivity';
import ServiceActivity from './ServiceActivity';
export default createMaterialTopTabNavigator({
Newsfeed:{screen: firstBottomNavigation},
Services:{screen: secondBottomNavigation }
},
{
initialRouteName:'Services',
swipeEnabled:true,
navigationOptions:({navigation})=>({
header:null
}),
tabBarOptions:{
activeTintColor:'#65FAE9',
inactiveTintColor:'white',
allowFontScaling:true,
indicatorStyle:{borderBottomColor:'#65FAE9', borderBottomWidth:4,},
style:{backgroundColor:'#515276',paddingBottom:5},
labelStyle:{fontWeight:'bold',marginTop:'40%'},
},
},
);
const firstBottomNavigation = createBottomTabNavigator({
FirstTab:{screen FirstTab},
SecondTab: {screen:SecondTab}
})
const secondBottomNavigation = createBottomTabNavigator({
ThirdTab:{screen ThirdTab},
SecondTab: {screen:SecondTab} //You can recycle screens
})
You can get creative , you can try nesting a toptabnavigator in a bottomnavigator ,etc . But don't make things too complicated because if it's complicated for you, imagine how hard is gonna be for the user