react-native pass data to parent navigation from second navigation - reactjs

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')

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

React Native rerendering UI after using array.map

I am attempting to have an icon switch its visual when clicked (like a checkbox). Normally in react native I would do something like this:
const [checkbox, setCheckbox] = React.useState(false);
...
<TouchableHighlight underlayColor="transparent" onPress={() => {setCheckbox(!setCheckbox)}}>
{added ? <MaterialIcons name="playlist-add-check" size={40} />
: <MaterialIcons name="playlist-add" size={40} />}
</TouchableHighlight>
However I have made some changes, and now I can't seem to replicate this behavior. I am using AsyncStorage class to storage and get arrays of objects for display. For simplification, in the example below I removed the storage code, and the objects each have an 'id' and an 'added' attribute, which is essentially the boolean value of the checkbox.
I am now attempting to update the icon shown to the user whenever it is pressed. I know the function is being called, but it will not update the icon. I am using array.map to create the list of icons. I created a demo here, and the code is below: https://snack.expo.dev/#figbar/array-map-icon-update
const templateObject = {
id: 0,
added: false,
};
const templateObject2 = {
id: 1,
added: true,
};
export default function App() {
const [savedNumbers, setSavedNumbers] = React.useState([]);
React.useEffect(() => {
setSavedNumbers([templateObject,templateObject2]);
}, []);
const populateSavedNumbers = () =>
savedNumbers.map((num, index) => <View key={index}>{renderPanel(num.id,num.added)}</View>);
const updateNumber = (id) => {
let capturedIndex = -1;
for(var i = 0; i < savedNumbers.length; i += 1) {
if(savedNumbers[i].id === id) {
capturedIndex = i;
break;
}
}
let _tempArray = savedNumbers;
_tempArray[capturedIndex].added = !_tempArray[capturedIndex].added;
setSavedNumbers(_tempArray);
}
const renderPanel = (id:number, added:boolean) => {
return (
<View>
<TouchableHighlight underlayColor="transparent" onPress={() => {updateNumber(id);}}>
{added ? <MaterialIcons name="playlist-add-check" size={40} />
: <MaterialIcons name="playlist-add" size={40} />}
</TouchableHighlight>
</View>
);
}
return (
<View>
<View>buttons:</View>
<View>{populateSavedNumbers()}</View>
</View>
);
}
This is a common React pitfall where things don't re-render when it seems like they should. React does shallow comparisons between new and old states to decide whether or not to trigger a re-render. This means that, when declaring a variable to simply equal a state variable which is an object or an array, a re-render is not triggered since those two variables now reference the same underlying data structure.
In this case, you are setting _tempArray to reference the array savedNumbers rather than creating a new array. Therefore, React's shallow comparison comes back as "equal", and it doesn't believe that a re-render is necessary.
To fix this, change this line:
let _tempArray = savedNumbers;
to this:
let _tempArray = [...savedNumbers];

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

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.

Fetching document as JSON using React-Native

I have found some similar solutions but not one that does exactly what I want.
Here is what I wanna do: I have a few documents saved as JSON in a server, I want to fetch those documents using React-Native and display them on my phone.
However, think about a solution when I don't have to change my code every time I upload a new document to the server. React-native should be able to fetch everything from the server, even the new documents, without having to add new lines of code in the return{}. Those documents might differ from each other, some includes only text, some include text and input fields, some include pictures, text and input fields.
If something is unclear please let me know in the comment section.
Any suggestion would be highly appreciated!
Example of JSON how it would look like:
{
"results":[
{
"contract":{
"title":"Contract test",
"content":"You can always follow the progress of your application by logging on the the application portal. Please note that all communication from DTU will take place via this portal. When we have sent you a message on the ..."
},
"fillable_fields": {
"FIELD_NAME_1": "FIELD_VALUE_1",
"FIELD_NAME_2": "FIELD_VALUE_2",
"FIELD_NAME_N": "FIELD_VALUE_N"
},
"picture":{
"medium":"https://www.healthcaredenmark.dk/media/11272/bridgeit_logo.png"
}
}
]
}
My code in React-Native:
class HomeScreen extends React.Component {
constructor() {
super();
this.state = {};
this.getRemoteData();
}
static navigationOptions = {
title: 'List of documents',
};
getRemoteData = () => {
const url = "https://demo8106568.mockable.io/results";
fetch(url)
.then(res => res.json())
.then(res => {
this.setState({
data: res.results
});
})
.catch(error => {
console.log("get data error from:" + url + " error:" + error);
});
};
capFirstLetter = (string) => {
return string.charAt(0).toUpperCase() + string.slice(1);
}
renderNativeItem = (item) => {
const contract =
this.capFirstLetter(item.contract.title);
//this.capFirstLetter(item.name.content);
return <ListItem
roundAvatar
title={contract}
subtitle={item.content}
avatar={{ uri: item.picture.thumbnail }}
onPress={() => this.onPressItem(item)}
/>;
}
onPressItem = (item) => {
this.props.navigation.navigate('Detail', {item: item})
}
render() {
return (
<View>
<FlatList
data={this.state.data}
renderItem={({item}) => this.renderNativeItem(item)}
/>
{/* <Button
title="Go Detail"
onPress={() => this.props.navigation.navigate('Detail', {source: "homescreen"})}
/> */}
</View>
);
}
}
class DetailScreen extends React.Component {
static navigationOptions = {
title: 'Content of selected'
};
render() {
const source = this.props.navigation.state.params.source;
const item = this.props.navigation.state.params.item;
let contract = "";
let img = "";
let inp = "";
let content ="";
if (item != null) {
contract = item.contract.title;
img = item.picture.medium;
content = item.contract.content;
inp = item.fillable_fields.FIELD_NAME_1;
}
return (
<View style={styles.container}>
<Text style={styles.text}>{contract} </Text>
<Image
style={{width: 300, height: 128}}
source={{uri: img}}
/>
<Text style={styles.text} > {content} </Text>
<TextInput style={{textAlign: 'center', borderWidth:1, marginBottom: 7, height: 50}} source={{uri: inp}}/>
<Button title="Go back to the list" onPress={this._goHome}/>
</View>
);
}
_goHome = async () => {
this.props.navigation.navigate('Home');
};
}
I understand what you are trying to accomplish. But I really don't think you can make it work like you want. You can compare it to calling a normal API endpoint. You will most likely have a method like:
getContracts() {
fetch('CONTRACTS_ENDPOINT').then(res => doSomethingWithContracts(res))
}
You already know that this data returns contracts and you already know what data to expect there. Therefore you can easy access fields like contract.name, or contract.date.
And when you want to call some other endpoint you will do something similar
getSomethingElse() {
fetch('OTHER_ENPOINT').then(res => ...)
}
You will know the data that comes with the OTHER_ENPOINT, so you can directly access its fields.
So my suggestion is, think of each of you document as a separate API endpoint. Of course, if you change your document, you will also need to change client side implementation, so for example if you rename contract.title to contract.otherWordForTitle then you will obviously need to change that on the client as well.
From what I know, what you want, to have the client always know the document structure without updating it in order to know that a document has changed, is not possible. But of course, I might be wrong and there can be a workaround :-)

React Native - setNativeProps() on parentElement.props.children = undefined

I'm developing a school management app for myself.
All students in my class are listed in a Flatlist with their parents' phone numbers beside to enable me to send them text messages when a student is absent.
I have a FlatList with Listitems, each of which contains a Touchopacity component with Text child inside.
On successful sending an sms to a student's parent (smsParent method) I want to setNativeProps on both TouchOpacity and its Text child (manipulate their style props). I use ref=(()=>...) to have reference to Touchopacity and then this.props.children (only 1 child) to get to its Text child.
Then however I cannot use setNativeProps (=undefined).
However, when I use ref=(()=>...) on Text as well and then refer to it, setNativeProps works /like in case of its parent/.
Why can't I use setNativeProps() on a child when refering to it by parentEl.props.children? (only 1 child, checked in debugger, it's properly identified)
Please read comments in smsParent method
/*sorry for inserting a snippet - code insertion was crazily formatted/
/**code simplified/
class SingleClassPage extends Component {
buttons = new Array();
constructor(props) {
super(props);
this.state = { students: [] };
this.smsParent = this.smsParent.bind(this);
}
componentDidMount() {
//fetch students from api and setState..
this._getStudentsList();
}
_getStudentsList() {
// ...
}
//flatlist item
ListEl(props) {
return (
<View>
<TouchableOpacity ref={el => { let a = props.item.attId + 'att'; props.buttons[a] = el; }}
style={[styles.buttonDef, (props.item.phone_parent ? styles.buttonBlue : styles.buttonGray)]}
onPress={() => { props.smsSendHandler(props.item, 'attendance', a) }}>
<Text style={props.item.phone_parent ? styles.buttonTextLight : styles.buttonTextDark}>
{props.item.smsAttSent ? 'sms sent' : 'sms send'}
</Text>
</TouchableOpacity>
</View>
)
}
render() {
return (
<View style={{ flex: 1, }}>
<FlatList
data={this.state.students}
extraData={this.state}
keyExtractor={item => item.attId}
renderItem={({ item }) => <this.ListEl buttons={this.buttons} item={item} smsSendHandler={this.smsParent} />}
/>
<BusyIndicator />
</View>
);
}
smsParent(student, msgCategory, smsButton) {
//call myjava module and upon successful callback call below:
let parEl = this.buttons[smsButton];
//childEl is an object with props.children set to text 'sms sent/send' when I watch its value in debugger
//so it's correctly identified
let childEl = parEl.props.children;
// WORKS
parEl.setNativeProps({ style: { backgroundColor: 'green' } });
// OOPS
childEl.setNativeProps({ style: { color: 'black' } });
}
}
edit1
Posting a screenshot of the error (also as response to Dyo's suggestion below - the same error Dyo...)
I think you have to iterate throught children to pass them nativeProps (even if there's only one child) :
smsParent(student, msgCategory, smsButton) {
//call myjava module and upon successful callback call below:
let parEl = this.buttons[smsButton];
React.Children.forEach(parEl.props.children, child => { child.setNativeProps({ style: { color: 'black' } }) });
parEl.setNativeProps({ style: { backgroundColor: 'green' } });
}

Resources