React native component state not clearing/ umounting - react-native

I am using a the same component (BottomSheet) on two different screens in my react navigation stack.
When I go from one page to another, it keeps the component state. How can I refresh the component when I browse to another screen?
const HomeScreen = () => {
...
const showSheet = data.reminderVehicles.length !== 0 ? true : false;
return (
<BottomSheet
firstSnapshot={160}
secondSnapshot={90}
renderContent={renderSheetContent()}
tapToOpenEnabled
headerClose={true}
hide={showSheet}
>
.....
</BottomSheet>
);
};
export default HomeScreen;
export const MyVehicleScreen = ({ navigation }) => {
return (
...
<BottomSheet
firstSnapshot={160}
secondSnapshot={90}
renderContent={renderSheetContent()}
tapToOpenEnabled
hide={false}
>
</BottomSheet>
</RACPageStructure>
);
};
BottomSheet component:
const BottomSheet = ({
children,
firstSnapshot,
secondSnapshot,
renderHeader,
renderContent,
backgroundColor,
tapToOpenEnabled,
headerClose,
hide,
}) => {
...
return (
<WrapperComponent onPress={toggleOpen}>
<Animated.View style={childrenStyle}>{children}</Animated.View>
{!hide && (
<PanGestureHandler onGestureEvent={handleSwipe}>
....
</Animated.View>
</PanGestureHandler>
)}
</WrapperComponent>
);
};
Navigation stack:
return (
<TabStack.Navigator
initialRouteName={initialRouteName}
tabBarOptions={{
...
}}
>
<TabStack.Screen
name={AppRoutes.Home.Name}
...
</TabStack.Screen>
<TabStack.Screen
name={AppRoutes.MyVehicle.Name}
...
</TabStack.Screen>

Related

React Native provider contexts and handling multiple contexts

So, I want to consume a context TaskContext in just two components. There is a way to do this, without wrapping my whole application with the TaskContext ? Furthemore, what would be the best way to handle multiples context ?
I've try wrapping a single component with the context by creating another function that returns a new component. For example:
const HomePageContext = (props) => {
return (
<TaskProvider>
<HomePage {...props}/>
</TaskProvider>
)
}
But with this approach, the data update in AddTask screen doesn't appear in HomePage.
(Disclaimer: I just want to create a homePage that contains a list of tasks. This tasks are created in another screen, AddTasks. When the task is created, the homepage should be update to display the new list, with all other tasks and the new one. Thus, i dont know what is the best approach to do this.)
My code is here. It's only for test and practice purpose.
import React, { createContext, useReducer, useContext} from 'react'
import { View, FlatList, Text, Button } from 'react-native'
import { createNativeStackNavigator } from '#react-navigation/native-stack'
import { createDrawerNavigator } from '#react-navigation/drawer'
import { NavigationContainer } from '#react-navigation/native'
const TaskContext = createContext({})
const initialState = [{ name: `Task name ${Math.floor(Math.random() * 100)}`, id: Math.floor(Math.random() * 100) }]
//contexto provider
const TaskProvider = ({ children }) => {
const reducer = (state, action) => {
switch(action.type) {
case 'addTask':
console.log([...state, action.payload])
return [...state, action.payload]
default:
return state
}
}
const [tasks, dispatch] = useReducer(reducer, initialState)
return (
<TaskContext.Provider value={ { tasks, dispatch } }>
{ children }
</TaskContext.Provider>
)
}
const HomePage = ({ navigation }) => {
const { tasks, dispatch } = useContext(TaskContext)
return (
<View>
<Text>Home</Text>
<FlatList
data={tasks}
keyExtractor={item => `${item.id}`}
renderItem={({ item }) => <Text>{item.id} - {item.name}</Text>}
/>
<Button
title='Add Task'
onPress={() => navigation.navigate('AddTask')}
/>
</View>
)
}
const Info = () => {
return (
<View>
<Text>Info</Text>
</View>
)
}
const AddTaskPage = () => {
const { dispatch } = useContext(TaskContext)
const newTask = {id: Math.floor(Math.random() * 100), name: `Task name ${Math.floor(Math.random() * 100)}`}
return (
<View>
<Text>addTaskPage</Text>
<Button
title='AddTask'
onPress={() => dispatch({
type: 'addTask',
payload: newTask
})}
/>
</View>
)
}
// createNavigators
const Stack = createNativeStackNavigator()
const Drawer = createDrawerNavigator()
const DrawerNavigation = () =>{
return (
<Drawer.Navigator>
<Drawer.Screen name='Home' component={HomePage}/>
<Drawer.Screen name='Info' component={Info}/>
</Drawer.Navigator>
)
}
// App component
export default App = () => {
return (
<TaskProvider>
<NavigationContainer>
<Stack.Navigator initialRouteName='Menu'>
<Stack.Screen name='Menu' options={{ headerShown: false }} component={DrawerNavigation} />
<Stack.Screen name='AddTask' component={AddTaskPage} />
</Stack.Navigator>
</NavigationContainer>
</TaskProvider>
)
}

how to finish modal animation in react-native-modal componentwillunmount?

I am looking for a solution to delay RN component unmount until react-native-modal animationOut is completed. This animation is triggered by setting isVisible=false.
With my current implementation the animation performs as expected on componentWillMount hook, however the problem is in componentWillUnmount the alert unmounts without an animation. I suspect this is because the component unmounts before the animation even has a change to start.
Here's the alert component:
//modal component
import Modal from "react-native-modal";
export default CustomAlert = props => {
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
setIsVisible(true)
return () => setIsVisible(false);
}, [])
return (
<Modal
isVisible={isVisible}
animationIn={'slideInUp'}
animationOut={'slideOutDown'}
>
<View
...
</View>
</Modal>
)
};
Here's how the custom alert needs to be used:
export default ApplicationScreen = ({ navigation }) => {
const [alert, setAlert] = useState(null);
...
const doSomething = () => {
...
//show alert
setAlert({ title: 'title', message: 'message'});
...
//hide alert
setAlert(null);
...
}
...
return (
<SafeAreaView style={styles.container}>
{alert &&
<CustomAlert
title={alert?.title}
message={alert?.message}
...
/>
}
...
</SafeAreaView>
)
}
The problem is that you are containing the CustomAlert in a conditional rendering. Once the alert is set to false, the Modal would dismount directly.
You could pass the alert state into CustomAlert as property and then into Modal. The 's property isVisible would hanlde the render of the modal for you with animation.
ApplicationScreen:
<SafeAreaView style={styles.container}>
<CustomAlert
title={alert?.title}
message={alert?.message}
isAlertVisible={alert? true : false}
/>
</SafeAreaView>
in CustomAlert:
return (
<Modal
isVisible={props.isAlertVisible}
animationIn={'slideInUp'}
animationOut={'slideOutDown'}
>
<View
...
</View>
</Modal>
)

Pass navigation from child component to parent getting TypeError

I'm facing an issue whenever i tried to navigate to another screens. I'm using the navigation in child component and it doesn't work even i passed the props to the parent component. This is my first time on using react navigation. I've tried many possible solution yet still can't solve this issue. I'm using react navigation 5 and i need help. I'm getting an error as such :
TypeError: undefined is not an object (evaluating 'this.props.navigation.navigate')
Home.js // Parent Component
class Home extends Component {
render() {
return (
<Cards
title="In Progress"
imgUri={require('../assets/CardProgress.png')}
navigateAction={() =>
this.props.navigation.navigate('SiteAudit')
}
)
}
}
Card.js // Child Component
const Cards = (props) => {
return (
<CardContainer
backgroundColor={props.backgroundColor}
onPress={() => {
props.navigation.navigateAction;
}}>
<CardWrapper>
<CardTitle>{props.title}</CardTitle>
<CardImage source={props.imgUri} />
</CardWrapper>
</CardContainer>
);
};
import React from 'react';
import {NavigationContainer} from '#react-navigation/native';
import {createStackNavigator} from '#react-navigation/stack';
import Dashboard from './screens/Dashboard';
import SiteAudit from './screens/SiteAudit';
const RootStack = createStackNavigator();
const DashboardStack = createStackNavigator();
const SiteAuditStack = createStackNavigator();
const DashboardScreen = () => {
return (
<DashboardStack.Navigator>
<DashboardStack.Screen name="Dashboard" component={Dashboard} />
</DashboardStack.Navigator>
);
};
const SiteAuditScreen = () => {
return (
<SiteAuditStack.Navigator>
<SiteAuditStack.Screen name="SiteAudit" component={SiteAudit} />
</SiteAuditStack.Navigator>
);
};
const Navigation = () => {
return (
<NavigationContainer>
<RootStack.Navigator initialRouteName="Dashboard">
<RootStack.Screen name="Dashboard" component={DashboardScreen} />
<RootStack.Screen name="SiteAudit" component={SiteAuditScreen} />
</RootStack.Navigator>
</NavigationContainer>
);
};
export default Navigation;
Edit your card view as follows
<Cards
title="In Progress"
imgUri={require('../assets/CardProgress.png')}
onPress={() => this.buttonPress()}
/>
const buttonPress = () => {
this.props.navigation.navigate('Site Audit');
};
Edit your button as follows,
<TouchableOpacity
style={styles.cardButton}
onPress={onPress}
<Text style={styles.cardTitle}>{this.props.title}</Text>
<Image style={styles.imageCard} source={this.props.imgUri} />
</TouchableOpacity>
Solved the issue, I just need to re-read the documentation of react navigation. I need to use the useNavigation that has been stated here https://reactnavigation.org/docs/use-navigation/
Cards.js // Child Component
const Cards = (props) => {
return (
<CardContainer
backgroundColor={props.backgroundColor}
onPress={props.navigationAction}>
<CardWrapper>
<CardTitle>{props.title}</CardTitle>
<CardImage source={props.imgUri} />
</CardWrapper>
</CardContainer>
);
};
Home.js // Parent Component
import {useNavigation} from '#react-navigation/native';
const Home = () => {
const navigation = useNavigation();
return (
<Cards
title="Completed"
backgroundColor="#0082C8"
navigationAction={() => {
navigation.navigate('Site Audit');
}}
/>
)
}

React Native insert one component into another

Is there a way to "inject" one RN component into another in a specific place.
Say I have this component:
const Original = () => {
return (
<View>
<Text>Hello</Text>
{InsertChildComponentHere}
</View>
)
}
const ChildComponent = () => {
return (
<View>
<Text>I am a child component</Text>
</View>
)
}
To inject a Component into a other Component (HOC) your component has to accept "Component" as params. You can write it like this:
const Original = (Component) => {
const newComponent = ({ ...props }) => {
return (
<Fragment>
<Text>Hello</Text>
<Component {...props} />
</Fragment>
);
};
return newComponent;
};
to create the HOC you can write:
const MyComponent = withOriginal(ChildComponent);

How to skip back/modify navigation

I have a stackNavigation which works great. 3 screens : DeviceList / DeviceDetail / DevideAdd. The normal paths is (1) DeviceList > DeviceDetail or (2) DeviceList > DeviceAdd > DeviceDetail.
But when user use the path (2), I want that the back button of the DeviceDetail screen go to DeviceList. Or for the moment, it's going to DevideAdd.
Here is my navigation:
const DeviceStackNavigator = createStackNavigator({
DeviceList: {
screen: DeviceList,
},
DeviceDetail: {
screen: DeviceDetail,
},
DeviceAdd: {
screen: DeviceAdd,
},
});
How to achieve it ?
The key attribute 'goBack()' is a dynamically generated string that is created each time you navigate to a new 'react-navigation' path.
So if you want to go from DeviceDetail to DeviceList, what you have to do is to pass the key of DeviceAdd down to DeviceDetail, and then call goBack() with the key.
DeviceAdd.js
render() {
const { state, navigate } = this.props.navigation;
return (
<View>
<Button title="Go to DeviceDetail" onPress={ () => {
navigate('DeviceDetail', { go_back_key: state.key });
}} />
</View>
);
}
DeviceDetail.js
render() {
const { state, goBack } = this.props.navigation;
const params = state.params || {};
return (
<View>
<Button title="Back to DeviceList" onPress={ () => {
goBack(params.go_back_key);
}} />
</View>
);
}