Passing parameter and reset it when leave current screen by React Navigation - reactjs

I try to pass a object type parameter from Home screen to the DailyActivity screen by touch the button that selected user Overlay in Home screen. But still showing last selected user's parameter data even though leave current(DailyActivity) to another screen.
So let me know these things..
How to reset parameter when move to another screen?
How to detect current screen name in child screen component?
Home screen
<Button
buttonStyle={{ margin: 5, backgroundColor: "#ef9a9a" }}
title="Daily Activity"
onPress={() => {
navigation.navigate("Daily Activity", { baby });
setIsOverlayed(!isOverlayed);
}}
/>
DailyActivity Screen
export default function DailyActivity({ route }) {
if ({ route }) {
return (
<View style={styles.container}>
<Text style={styles.text}>Daily Activity Screen</Text>
<Text>{route.params.baby.firstName}</Text>
</View>
);
} else {
return (
<View style={styles.container}>
<Text style={styles.text}>Daily Activity Screen</Text>
<Text>params not exist</Text>
</View>
);
}
}

import { useFocusEffect } from '#react-navigation/native';
useFocusEffect(
React.useCallback(() => {
// Do something when the screen is focused
return () => {
// Do something when the screen is unfocused
navigation.setParams({ contactId: null }) // reset parametr that you need
};
}, [setParams]),
);

do like this
In Home screen:
navigation.navigate("Daily Activity", { baby:baby });
setIsOverlayed(!isOverlayed);
and in daily activity screen make it as null after getting baby name
navigation.setParams({baby: null});
If you are using hooks then call this method
import {useIsFocused} from '#react-navigation/native';
const isFocused = useIsFocused();
export default function DailyActivity {
useEffect(() => {
!isFocused && resetNavigationMethod();
}, [isFocused]);
... your codes
}
useIsFocused is used to whenever you are tried to changing screens, this this function will trigger. If you are going back, then isFocused will return as false.

Related

Passing a button select choice back to previous screen

So after some research, I have learned how to make a button that will take the user to another screen, and provide them a text input where they can enter some words, then on pushing the done button take them back to the previous screen where what they typed will be displayed. But for my particular needs, I am trying to figure out how to instead of a text input have a selection of buttons, such as "large, medium, small" and have that button select the data that would be displayed instead, and return them to the previous page where it is displayed.
initial screen
function HomeScreen( route ) {
navigation = useNavigation();
React.useEffect(() => {
if (route.params?.post) {
}
}, [route.params?.post]);
return (
<View>
<Pressable
title="Create post"
onPress={() => navigation.navigate('CreatePost')}
>
<Text style={{ margin: 10 }}>Post: {route.params?.post}</Text>
</Pressable
</View>
);
}
button selection screen
function CreatePostScreen( route ) {
const navigation = useNavigation();
const [postText, setPostText] = React.useState('');
return (
<>
<Pressable
title="Done"
onPress={() => {
navigation.navigate({
name: 'Home',
params: { postText },
merge: true,
});
}}
>
<Text>
Large
</Text>
</Pressable>
</>
);
}
any insight is greatly appreciated.
You can pass the data in form of object
{post: postText,
buttonType: 'medium'}
For getting the data
React.useEffect(() => {
if (route.params?.post) {
var buttonType= route.params?.buttonType
}
}, [route.params?.post]);
You can store the button type in a variable or state
var buttonType = route.params?.buttonType
Or You can try with useState() hooks
const [buttonType, setButtonType]=useState("")
setButtonType(route.params?.buttonType)
The for using it just do the following
<Text>{buttonType}</Text>
Please follow the React-Documentation

TextInput /Input element looses focus on key press

I'm currently developing an application using React Native.
This trial app has a component that has a TextInput and two buttons (ADD and DELETE).
When I press the ADD Button, a new component appears. If I press the DELETE Button that the same component disappears.
The screen is like the photo bellow:
I control the TextInput with the index which is the same number as the index of the component.
My question is: why can't I enter some text as usual in this code?
I have to focus the cursor every time I enter 1 word.
I lose a flashing vertical bar(I check in the photo below) in the input area every time I press a key.
How can I resolve this problem?
And, I want to control the inputted value from TextInput with array[] not object{} because in case of an array is easier to delete a component sliding index and value like an explanation below:
I have no idea to control index and value with an, object and it's complicated for my skill now, but if there are some nice ways to resolve using object, I hope to know it.
Here is the code:
import React, { useState } from "react";
import { View, Text, Button, TextInput, StyleSheet } from "react-native";
function Item({ number, handleInput, handleAdd, handleDelete, index }) {
return (
<View style={styles.list}>
<Text>{index}</Text>
<TextInput
style={{ borderWidth: 1 }}
value={number[index]}
onChange={(e) => {
handleInput(index, e.nativeEvent.text);
}}
></TextInput>
<Button
title="ADD"
onPress={() => {
handleAdd();
}}
/>
<Button
title="DELETE"
onPress={() => {
handleDelete(index);
}}
/>
</View>
);
}
export default function TestStateArray() {
const [count, setCount] = useState(1);
const [number, setNumber] = useState([]);
function handleAdd() {
setCount((v) => v + 1);
}
function handleDelete(index) {
setCount((v) => v - 1);
setNumber((v) => {
const ret = v.slice();
ret.splice(index, 1);
return ret;
});
}
function handleInput(index, text) {
setNumber((v) => {
const ret = v.slice();
ret[index] = text;
return ret;
});
}
return (
<View>
{Array.from({ length: count }, (_, i) => (
<Item
number={number}
handleInput={handleInput}
handleAdd={handleAdd}
handleDelete={handleDelete}
key={i + "-" + number}
index={i}
/>
))}
</View>
);
}
const styles = StyleSheet.create({
list: {
margin: 10,
padding: 10,
backgroundColor: "#ddd",
},
});
After I have some answer and some comment, I tried changing the code like bellow, but it still has the same problem...
// onChange={(e) => {
// handleInput(index, e.nativeEvent.text);
onChangeText={(text) => {
handleInput(index, text);
}}
node : 12.18.3
react native : 4.10.1
expo : 3.22.3
The above issue occurred because your handle change function is wrong.
Please change...
const [number, setNumber] = useState({}); // change array to Object in useState.
Replace your handler with below function:
function handleInput(index, text) {
setNumber({ ...number, index: text });
}
React Native has onChangeText event on TextInput component can you try that one?

React Native scrollview infinitely rerendering

I'm new to react native and I'm creating a to do list app. the component I wrote keeps infinitely re-rendering when I initialize Todos state to something that isn't an empty array. This results to an error where react stops it. I have no idea what is causing the infinite loop as I haven't included any recursion or explicit loops. I added a few console.log()'s everywhere and found that deleteTodo and toggleTodo keep getting called automatically until the application crashes.
When Todos state is initialized to an empty array I get an error saying that getTodos.map is not a function.
The component is only expected to re-render whenever the state is updated to either modify , add or delete a task.
import React, { useState } from 'react'
import { Platform, StyleSheet, Text, View, TouchableHighlight } from 'react-native'
import { State, ScrollView } from 'react-native-gesture-handler'
/* All required imports are correctly added */
let id = 0
function TaskRow(props) {
return (
<View>
{/* empty button acting as checkbox */}
<TouchableHighlight>
<Text />
</TouchableHighlight>
{/* main text area, displays task and all details + tags*/}
<TouchableHighlight onPress={props.toggle(props.key)}>
<Text>{props.task}</Text>
</TouchableHighlight>
{/* delete button */}
<TouchableHighlight onPress={props.delete(props.key)}>
<Text>Delete</Text>
</TouchableHighlight>
</View>
)
}
export default function TodoList() {
const [
getTodos,
setTodos,
] = useState([
{ key: id, task: 'this is a task', checked: false },
])
const addTodo = (taskInput = 'sample') => {
id++
setTodos(getTodos.push({ key: id, task: 'task', checked: false }))
}
const deleteTodo = (deleteID) => {
setTodos(getTodos.filter((match) => match.key !== deleteID))
}
const toggleTodo = (toggleID) => {
setTodos(
getTodos.map((match) => {
if (match.key == toggleID) return { key: match.key, task: match.task, checked: !match.checked }
}),
)
}
return (
<View>
<TouchableHighlight onPress={addTodo}>
<Text>Add task</Text>
</TouchableHighlight>
<ScrollView>
{getTodos.map((task) => (
<TaskRow
key={task.key}
task={task.task}
delete={deleteTodo}
toggle={toggleTodo}
/>
))}
</ScrollView>
</View>
)
}
You have a problem in your TaskRow component, on each render you are actually calling the functions instead of declaring an handler for events. Please chenge these two lines in this way:
// ...
<TouchableHighlight onPress={() => props.toggle(props.key)}>
// ...
<TouchableHighlight onPress={() => props.delete(props.key)}>
Another problem is this:
setTodos(getTodos.push({ key: id, task: 'task', checked: false }))
the .push() does not return the array so you should do something like:
setTodos([...getTodos, { key: id, task: 'task', checked: false }])

react-native pass data to parent navigation from second navigation

Here is a part of the code of second screen:
state = {
hasCameraPermission: null,
barcodeValue : ""
}
FunctionToOpenFirstActivity = () =>
{
this.props.navigation.navigate('First', { barcodeValue: this.state.barcodeValue });
}
//after barcode was scanned
handleBarCodeScanned = ({ type, data }) => {
this.state.barcodeValue = data;
alert(this.state.barcodeValue);
this.FunctionToOpenFirstActivity();
}
Here is a part of the code of parent screen in render ()
<View style={styles.firstrow}>
<View style={styles.inputWrap}>
<Text style={styles.label}>Barcode Value</Text>
<TextInput style={styles.input}>{this.props.barcodeValue}</TextInput>
</View>
</View>
this.props.barcodeValue is blank, anyone knows what is the problem?
as you can find inside the documentation https://reactnavigation.org/docs/en/params.html, you can read the params that passed from previous view using this.props.navigation.getParam(paramName, defaultValue), defaultValue is optional
in your case, try changing this.props.barcodeValue to this.props.navigation.getParam('barcodeValue')

How to properly update/re-render a component that is not a child React Native?

I'm using a react-navigation. More specifically, I have a materialTabNavigator nested inside of a drawerNavigator. Each tab is in itself a stackNavigator. I have a button in homeScreen, that navigates to makePost.js. There I take in information and store it to Async storage using a simple wrapper.
In Posts.js there's a FlatList displaying each post as a component. The data for the FlatList is initially set correctly after making a request from Async Storage. The problem is that this only happens when the app is first opened. I have tried many different approaches to solve this. The only way so far I've found is to continuously setState in ComponentDidUpdate() in Posts.js. Obviously this is problematic, because it re-renders constantly. I can set a flag to stop is from rendering, but then it will not re-render again.
Ultimately, what I'd like to happen is that when I hit the user is done entering their information and is ready to make a post, they hit the button in makePost.js, and the data in the FlatList of Posts.js is update.
I've tried to pass parameters using navigation, does not work, parameters get lost somewhere, probably because of the nested navigators.
I could really used some guidance on the proper way to accomplish this.
( Navigators; not sure why this is forcing to one line )
---drawer
--tabNav
-home
homeScreen.js
makePost.js
-posts
posts.js
-messages
--drawer1
--drawer2
//Posts.js
export default class Posts extends React.Component {
state = {
rows: [
{id: 0, text: "dog"},
],
}
componentDidMount() {
this.loadState();
}
loadState = () => {
var value = store.get('posts').then((res => {
if (res === null) {
res = [{id: 0, text: "default"}]
} else {
res = res
}
this.setState({rows: res})
}))
}
componentDidUpdate() {
this.loadState();
}
renderItem = ({item}) => {
return (
<BoardTab style={styles.row} />
)}
render() {
return (
<View style={styles.view}>
<FlatList
ListFooterComponent={this.renderFooter}
style={styles.container}
data={this.state.rows}
renderItem={this.renderItem}
keyExtractor={extractKey}
>
</FlatList>
<BoardScreenFooter />
</View>
);
}
And Posts.js button looks like this:
<TouchableOpacity
onPress={ () => {
this._onPressButton
this.storeFunc(this.state.newPost)
const retval = this.state.rows
this.props.navigation.navigate('Board',
{rowsID: retval});
}
}>
<Icon
reverse
name='md-camera'
type='ionicon'
color='green'
size={12}
/>
</TouchableOpacity>
storeFunc(newObj) {
newObj.id = newObj.id + 1
store.push('posts', newObj)
store.get('posts').then((res) => {
this.setState({rows: res})
})
}
Rapidly, i would say: use Redux. It alloq you to have global state in your app, which mean you can access the state anywhere (And also set them anywhere)
When opening the app, you get the data from the AsyncStore into the Redux store. You listen to the redux state (Which will be a props in your component) and display your list. When modifying your list in the other tab, you need to do 2 things:
Store the new data in the AsyncStorage
Update the state in the redux store. Since Posts.js will be listening at the redux store (as a props), it will re-render each time your data will change
A simple way to re-render a React-Navigation screen view on navigating to it:
All credit goes to Andrei Pfeiffer, Jul 2018, in his article: "Handle Tab changes in React Navigation v2" https://itnext.io/handle-tab-changes-in-react-navigation-v2-faeadc2f2ffe
I will reiterate it here in case the above link goes dead.
Simply add a NavigationEvents component to your render function with the desired listener prop:
render() {
return (
<View style={styles.view}>
<NavigationEvents
onWillFocus={payload => {
console.log("will focus", payload);
this.loadState();
}}
/>
<FlatList
ListFooterComponent={this.renderFooter}
style={styles.container}
data={this.state.rows}
renderItem={this.renderItem}
keyExtractor={extractKey}
>
</FlatList>
<PostScreenFooter />
</View>
);
}

Resources