Objects are not valid as a React child found: object with keys {cca2, currency, callingCode, region, subregion, flag, name} - react-native

I am using component with AppPicker that selects a country then passes the selected country to another component through navigation.navigate('component', { parameter: parameter}), my problem is, I am getting Objects are not valid as a React child. the AppPicker's onselect method returns an array of objects as shown below and I have a function that extracts only the country name, the country name is stored in country state and I pass that to another component for display. Can anyone help with this problem. my components are shown below:
Covid.js
import React, { useState } from 'react';
import { StyleSheet, TouchableOpacity, Text, View } from 'react-native';
import CountryPicker from 'react-native-country-picker-modal'
export default function App(props) {
const [country, setCountry] = useState(null) ;
const getCountry = (country) => {
return country.name;
}
return (
<View style={styles.container}>
{/* <TouchableOpacity onPress={ () => props.navigation.navigate('CovidCountryData', { selectedCountry: country})}> */}
<Text>
Welcome to Country Picker !
</Text>
<CountryPicker
withFilter
onSelect={(country) => {
const countrySelect= getCountry(country)
setCountry(countrySelect);
console.log(countrySelect);
props.navigation.navigate('CovidCountryData', { selectedCountry: country})
}}
/>
{/* </TouchableOpacity> */}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
the onselect returns the array shown below
Object {
"callingCode": Array [
"213",
],
"cca2": "DZ",
"currency": Array [
"DZD",
],
"flag": "flag-dz",
"name": "Algeria", =================> this is the country name I am extracting..
"region": "Africa",
"subregion": "Northern Africa",
}
CovidCountryData.js
import React from 'react'
import {Text, StyleSheet} from 'react-native';
export default function CovidCountryData(props) {
//console.log(props.navigation)
let Country = props.navigation.getParam('selectedCountry'); //selectedCountry
console.log(Country) ;
return (
<>
<Text>You have selected the country shown below</Text>
<Text>{Country}</Text>
</>
)
}

Here, you are sending through the navigation the whole country object received from the onSelect function inside your CountryPicker, because the variable shares the same name as the one you're keeping in state. Refactor it this way to understand the changes:
<CountryPicker
withFilter
onSelect={(selectedCountry) => {
const countrySelect= getCountry(selectedCountry)
setCountry(countrySelect);
console.log(countrySelect);
props.navigation.navigate('CovidCountryData', { selectedCountry: country}) // before even if you sent the country as param here, it wasn't the country from your state, but the one which is now called selectedCountry
}}
/>

Related

Is it possible to Get 'style' property values from React Native Element AFTER rendering using useRef?

I'm trying to dynamically apply margin & padding to a View, based on a ref'd TextInput's borderRadius. I am new to React coming from Xamarin where this type of thing is common.
I'm not sure if I have the correct approach, but I have seen some examples of people deriving style values from useRef.
Here is my custom LabelInput component:
import React, {useState} from 'react';
import {
View,
Animated,
StyleSheet,
ViewProps,
} from 'react-native';
import {Colors} from '../../../resources/colors';
import Text from './Text';
import TextInput from './TextInput';
import {TextInputProps} from 'react-native/Libraries/Components/TextInput/TextInput';
import {isNullOrWhiteSpace} from '../../../utils/stringMethods';
import {TextProps} from 'react-native/Libraries/Text/Text';
interface LabeledInputProps {
label: string;
error: string;
onChangeText: (text: string) => void;
placeholder?: string;
inputValue: string;
mask?: (text: string) => string;
validator?: (text: string) => string;
onValidate?: (value: string) => void;
viewProps?: ViewProps;
textProps?: TextProps;
errorTextProps?: TextProps;
inputProps?: TextInputProps;
}
export default function LabeledInput(props: LabeledInputProps) {
const inputRef = React.useRef<any>(null);
const [dynamicStyle, setDynamicStyle] = useState(StyleSheet.create({
dynamicContainer:{
marginHorizonal: 0,
paddingHorizonal: 0,
}
}));
const changeTextHandler = (inputText: string) => {
const displayText = props?.mask ? props.mask(inputText) : inputText;
props.onChangeText(displayText);
// ultimately not the exact behavior I'm after, but this is a simple example.
var test = inputRef.current.props.style;
// props.style always returns undefined,
// there doesn't appear to be a 'props' property on the 'current' object when debugging.
setDynamicStyle(StyleSheet.create({
dynamicContainer:{
marginHorizonal: test.borderRadius, // I want the padding/margin of this element to be
paddingHorizonal: test.borderRadius,// dynamically set based on the inputRef's borderRadius
}
}))
};
return (
<View
{...props.viewProps}
style={[
props.viewProps?.style,
localStyles.container,
]}>
<TextInput
ref={inputRef}
{...props.inputProps}
placeholder={props.placeholder}
style={localStyles.input}
onChangeText={changeTextHandler}
value={props.inputValue}
/>
<Animated.View
pointerEvents={'none'}>
<Text
{...props.textProps}
style={[props.textProps?.style, animatedStyles.label]}>
{props.label}
</Text>
</Animated.View>
{/* {stuff} */}
</View>
);
}
const localStyles = StyleSheet.create({
container: {
backgroundColor: 'blue',
justifyContent: 'flex-start',
flex: 1,
},
label: {
fontWeight: 'bold',
marginBottom: 8,
},
input: {
padding: 8,
},
error: {
backgroundColor: 'pink',
fontSize: 12,
paddingHorizontal: 8,
color: Colors.danger,
marginTop: 4,
},
});
const animatedStyles = StyleSheet.create({
label: {
fontSize: 16,
fontWeight: 'normal',
},
});
Here is my custom LabelInput component with forwardRef() implemented:
import React, {ForwardedRef, forwardRef} from 'react';
import {TextInput as NativeTextInput, TextInputProps} from 'react-native';
import {useGlobalStyles} from '../../../resources/styles';
const TextInput = (
props: TextInputProps,
ref: ForwardedRef<NativeTextInput>,
) => {
const styles = useGlobalStyles();
return (
<NativeTextInput
{...props}
ref={ref}
style={[styles.textInput, props.style]}
placeholderTextColor={styles.textInput.borderColor}
onChangeText={(text: string) => {
if (props.onChangeText) {
props.onChangeText(text);
}
}}
/>
);
};
export default forwardRef(TextInput);
I've tried referencing inputRef from different hooks, like useCallback & useEffect.
var test = inputRef.current.props.style; always returns undefined. And there doesn't appear to be a 'props' property on the 'current' object when debugging.
The link you mentioned contains two files with inputRef. Since inputRef is in parent component and use ref prop to pass inputRef, this will not work. ref is not available as prop. If you still want to use ref as prop, then use forward ref in child component as access the ref as second argument or you can use any other prop name to pass ref i.e. innerRef. You can read more in react documentation. Forward Refs
According to the code you attach in code sandbox, i think you are trying to access input styles in two components: App and LabeledInput. You should use one ref in main component and use it in LabelInput component. If you still want to have separate refs then you can ref callback function and attach the node with both refs.
const attachRef = (node: NativeTextInput) => {
inputRef.current = node;
ref.current = node;
};
return <TextInput ref={attachRef} />;
The correct type for inputRef.current is TextInputProps.
const inputRef = useRef() as MutableRefObject<TextInputProps>;
I have updated the code sandbox. I was able to access input field styles in both components. Hope this solves your problem.

Nested Lists using FlatList or SectionList

Heres my problem. I need to use react navigator to enter a new page with the appropriate data from a FLatList or SectionList using route.params.
Normally that would be no problem for me, but I want a more complex list with new lists in the list (Ideally using json in the future). I want to make a List that displays and sort animals into categories in a listed format. When you touch the desired animal you're forwarded to a page displaying info and facts aboat that animal.
The list of data looks like this (its shortend, but this is the template):
const species= [
{
title: 'SpeciesGroup1',
data: [
{
id: 1,
title: 'Species1',
}
],
},
{
title: 'SpeciesGroup2',
data: [
{
id: 1,
title: 'Species2',
}
],
},
];
This is the screen that diplays the data. AppList is a FlatList component. Everything is displayed as I want it. I've also tried using SectionList and that worked too.
import React from 'react';
import {
StyleSheet,
View,
FlatList,
} from 'react-native';
import AppList from '../components/AppList';
import AppText from '../components/AppText';
import Screen from '../components/Screen';
import routes from '../navigation/routes';
function SpeciesListScreen({ navigation }) {
return (
<Screen>
<FlatList
data={ species }
renderItem={({ item }) => (
<View style={ styles.container }>
<AppText textType='header'>{ item.title }</AppText>
<AppList items={item.data} onPress={ () => navigation.navigate( routes.SPECIES, item.data )} numberOfColumns={ 2 } />
</View>
)}
/>
</Screen>
);
}
const styles = StyleSheet.create({
container: {
padding: 20,
}
});
export default SpeciesListScreen;
Until now eveything works and loads as it should. The problem comes when i want to display the information in the SpeciesScreen. For some reason I can't access the info in the data array, in this case "title". The page loads perfectly fine. Just without the data.
The screen showing the species info looks like this:
import React from 'react';
import {
StyleSheet,
View,
FlatList,
} from 'react-native';
import AppText from '../components/AppText';
import Screen from '../components/Screen';
function SpeciesScreen({ route }) {
const animal = route.params;
return (
<Screen>
<AppText textType='header'>{ animal.title }</AppText>
</Screen>
);
}
export default SpeciesScreen;
Does anyone have any tips or solutions?
The way you pass the route params is incorrect. The route params are supposed to be an object, but item.data is an array.
You can correct this as follows.
<AppList items={item.data} onPress={ () => navigation.navigate( routes.SPECIES, { species: item.data} )} numberOfColumns={ 2 } />
You can access them as follows.
const animal = route.params.species[0]
If you know that this will always be just one object, you could do this as a preprocessing and just pass the object to the route params. If you got multiple objects, then you might want to loop over it.

React Native: Variable state not updated on first click

I am new in react-native and i am working on an app.
The below code is a simple react-native app which has a custom component with custom events.
But the problem is the variable state is not updated on the first click on the component. But when i click on the second item, The state of the variable is updated.
Please find the code and screenshot below.
App.js
import React, {useState} from 'react';
import { Text, SafeAreaView, ToastAndroid } from 'react-native';
import Dropdown from './components/dropdown';
const app = () => {
const [ itemData, setItemData ] = useState('');
return (
<SafeAreaView style={{ margin: 50 }}>
<Dropdown
onPressItems={(item) => {
ToastAndroid.show('item: ' + item, ToastAndroid.LONG)
setItemData(item)
ToastAndroid.show('setItem: ' + itemData, ToastAndroid.LONG)
}}/>
</SafeAreaView>
);
}
export default app;
Dropdown.js
import React, { useState } from 'react';
import { TouchableOpacity, Text } from 'react-native';
const Dropdown = (props) => {
return (
<TouchableOpacity onPress={() => { props.onPressItems('this is sample data') }}>
<Text>Sample Text</Text>
</TouchableOpacity>
);
}
export default Dropdown;
Screenshot
Code: https://snack.expo.dev/#likithsai/custom-component
Please help me on this issue. Thanks.
useState() hook changes the state asynchronously. so you can't make sure that the state will be changed immediately after calling setItemData() function.
Try useEffect to run a side effect whenever the state changes.
useEffect(() => {
ToastAndroid.show("setItem: " + itemData, ToastAndroid.LONG);
}, [itemData]);
However, this code will show the toast on the component mount. to prevent it try something like this:
const isInitialMount = useRef(true);
useEffect(() => {
if (isInitialMount.current) {
isInitialMount.current = false;
} else {
ToastAndroid.show("setItem: " + itemData, ToastAndroid.LONG);
}
}, [itemData]);
Your code is working just as expected. One of the hooks that are useful to watch for state updates is useEffect. You can add this to your code and see it's working properly:
const app = () => {
const [ itemData, setItemData ] = useState('');
React.useEffect(() => {
console.log('updated itemData:', itemData)
}, [itemData])
return (
<SafeAreaView style={{ margin: 50 }}>
<Dropdown
onPressItems={(item) => {
ToastAndroid.show('item: ' + item, ToastAndroid.LONG)
setItemData(item)
ToastAndroid.show('setItem: ' + itemData, ToastAndroid.LONG)
}}/>
</SafeAreaView>
);
}
You need to take into consideration that useState updates are asynchronous, which means the change won't be reflected immediately.

How to render my data inside a table and rendering real-time data (like stock price)

Nifty50 is a component of my react-native application in which my objective is to display the data from the json-server, which contains data in this format:
{
"nifty50":
[
{
"CompanyName": "Adani Ports and Special Economic Zone Ltd.",
"Industry": "SERVICES",
"Symbol": "ADANIPORTS",
"Series": "EQ",
"ISINCode": "INE742F01042\r"
},
{
"CompanyName": "Asian Paints Ltd.",
"Industry": "CONSUMER GOODS",
"Symbol": "ASIANPAINT",
"Series": "EQ",
"ISINCode": "INE021A01026\r"
},
...
]
}
The Nifty50 component is as follows:
import React, { Component } from 'react';
import { FlatList} from 'react-native-gesture-handler';
import { ListItem } from 'react-native-elements';
import { connect } from 'react-redux';
const mapStateToProps = state => {
return {
nifty50 : state.nifty50
}
}
class Nifty50 extends Component
{
render()
{
const renderStocks = ({item, index}) => {
return(
<ListItem
//onPress = {() => navigate('Details', {isinCode : item.isinCode})}
key = {index}
title = {item.Symbol}
subtitle = {item.CompanyName}
hideChevron = {true}
/>
);
}
return(
<FlatList
data = {this.props.nifty50.nifty50}
renderItem = {renderStocks}
keyExtractor = {item => item.ISINCode}
/>
);
}
}
export default connect(mapStateToProps)(Nifty50);
But this renders the "Symbol" and "Company Name" in simple list form, but I need to render them in tabular form and to show the details of each stock such as price and Market Capital in real-time.
Instead of ListItem, return a View.
<TouchableOpacity>
<View>
// All details goes here
</View>
</TouchableOpacity>
I also suggest putting your renderStocks() outside the render() method.

how to pass form data from screen1 to screen2 in react native?

how to pass form data from screen1 to screen2 in react native ? I have following code in scrren1 I want posted amount data in screen2. Please let me know how can I pass data on screen2 and receive it in react native?
import React, { Component } from 'react';
import { Button, View, Text, StyleSheet } from 'react-native';
import t from 'tcomb-form-native'; // 0.6.9
const Form = t.form.Form;
const User = t.struct({
amount: t.String,
});
const formStyles = {
...Form.stylesheet,
formGroup: {
normal: {
marginBottom: 10
},
},
controlLabel: {
normal: {
color: 'blue',
fontSize: 18,
marginBottom: 7,
fontWeight: '600'
},
// the style applied when a validation error occours
error: {
color: 'red',
fontSize: 18,
marginBottom: 7,
fontWeight: '600'
}
}
}
const options = {
fields: {
amount: {
label: "Enter Amount You want to Top up",
error: 'Please add amount to proceed ahead!'
},
},
stylesheet: formStyles,
};
class HomeScreen extends Component {
static navigationOptions = {
title: 'Home',
};
handleSubmit = () => {
const value = this._form.getValue();
console.log('value: ', value);
}
render() {
return (
<View style={styles.container}>
<Form
ref={c => this._form = c}
type={User}
options={options}
/>
<Button
title="Pay Now"
onPress={this.handleSubmit}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
justifyContent: 'center',
marginTop: 50,
padding: 20,
backgroundColor: '#ffffff',
},
});
export default HomeScreen;
It depends if you want to pass data between Parent to Child, Child to Parent or Between Siblingsā€Š
I suggest you to read Passing Data Between React Components, old but this article did help me to understand the logic behind passing data as it's not as easy to implement as in other programming languages.
Excerpt using props:
class App extends React.Component {
render() {
[... somewhere in here I define a variable listName
which I think will be useful as data in my ToDoList component...]
return (
<div>
<InputBar/>
<ToDoList listNameFromParent={listName}/>
</div>
);
}
}
Now in the ToDoList component, use this.props.listNameFromParent to access that data.
You have many ways to send informations from one screen to another in React Native.
eg.
Use React Navigation to navigate between your scenes. You will be able to pass params to your components, which will be accessible in the navigation props when received.
this.props.navigation.navigate({routeName:'sceneOne', params:{name} });
You can also send directly props to a component, and treat them in it. In your render section of your first component, you could have something like this :
<myComponent oneProps={name}/>
In that example, you will receive the props "oneProps" in your second component and you will be able to access it that way :
type Props = {
oneProps: string,
}
class myComponent extends React.Component<Props> {
render() {
console.log('received sent props', oneProps);
return (
<View> // display it
<Text>{this.props.oneProps}</Text>
</View>
);
};
}
These are only two effective solutions, but there are a lot more.
Hope it helped you :)
Have a good day