Strange behavior using useState to load data into a object state - react-native

i have broken my head trying to understand a problem with my code. I'm new with React Native so there may be a standard behavior that i am unaware of. This is my problem:
In my component i have a useEffect() to load my data like "componentDidMount":
useEffect( () => {
async function loadDadosLista(){
let listaRecebida = await getListaByID(route.params.idLista);
setLista(listaRecebida);
};
loadDadosLista();
}, []);
My function works correctly, the function getListaById accesses my realm.db and return my object lista. After that I can access the data and associate it with components of type TextInput. My real problem is that any change the i do in any component using properties of lista, overwrites all data leaving only the one that has been modified. I'm using spread operator but apparently it doesn't work. Below is my complete code for better understanding.
function ListConfig(){
const [lista, setLista] = useState({});
useEffect( () => {
async function loadDadosLista(){
let listaRecebida = await getListaByID(route.params.idLista);
setLista(listaRecebida);
};
loadDadosLista();
}, []);
return(
<View style={styles.container}>
<View style={[styles.containerLinha, styles.linha2]}>
<View style={styles.inputLocal}>
<TextInput
name='estabelecimento'
placeholder='Venda do seu Francisco'
placeholderTextColor={theme.colors.cinzaPrimario}
style={styles.textInputLocal(theme)}
value={lista.estabelecimento}
maxLength={25}
onChangeText={ (value) => {
setLista({
...lista,
estabelecimento: value
})
}}
textAlignVertical='bottom'
/>
<IconLocation width={20} height={24} />
</View>
</View>
<View style={styles.containerNotif}>
<Text style={styles.textoNotif(theme)}>
Me notifique 20 minutos antes
</Text>
<ToggleSwitch
isOn={lista.notificacaoAtiva}
onColor={theme.colors.cinzaSecundario}
offColor={theme.colors.cinzaSecundario}
thumbOnStyle={{
backgroundColor: theme.colors.destaque
}}
size="medium"
onToggle={(isOn) => {
setLista({
...lista,
notificacaoAtiva: isOn
});
}}
/>
</View>
</View>
);
}
export default ListConfig;
My object lista have this properties:
{
estabelecimento: 'nameOfEstabelecimento',
notificacaoAtiva: true
}

Related

Could not find "client" in the context or passed in as an option. Wrap the root component in an <ApolloProvider>, or pass an ApolloClient instance

This is my first Apollo Project, but I have been working with it for awhile, and things had been pretty smooth in terms of Apollo, until recently.
While I understand how Apollo works, I obviously do not know all the minutae involved with it, and I am unclear I suppose on exactly how the project is supposed to be wrapped.
Below is my App.js, when it worked as anticipated...
// Auth for token
const authLink = setContext((_, { headers }) => {
const token = state
return {
headers: {
...headers,
authorization: token ? `${token}` : ''
}
}
})
// Initialize Apollo Client
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
});
const Stack = createNativeStackNavigator();
export default function App() {
// Other irrelevant things like fonts...
const ErrorPrompt = (props) => {
return(
<View>
<View style={{height: 50, width: '100%', backgroundColor: "#534FFF"}}/>
<Text style={{...Template.title, fontSize: 30}}>Something happened!</Text>
<Text style={{...Template.subTitle, fontSize: 20}}>We apologize for the inconvenience</Text>
<Text style={{...Template.title, fontSize: 20, color: '#534FFF'}}>{props.error.toString()}</Text>
<View style={{marginLeft: 30}}>
<Text style={{...Template.subTitle, fontSize: 15, marginRight: 30, marginLeft: 0}}>An email has been sent to the Support Team to attempt to prevent this error from occurring again. We thank you for your support and patience</Text>
</View>
<View style={{marginTop: 50}}>
<Button onPress={props.resetError} title={'Reload Application'} />
</View>
</View>
)
}
if(!loaded){
return null
}
try{
return (
<ErrorBoundary
FallbackComponent={ErrorPrompt}
>
<NavigationContainer>
<ApolloProvider client={client}>
<RecoilRoot>
<IconRegistry icons={EvaIconsPack} />
<ApplicationProvider {...eva} theme={{...eva.light, ...theme}}>
<PaperProvider>
<KeyboardAvoidingView
behavior="padding"
enabled
style={{flexGrow:1,height:'110%'}}
>
<View style={AppStyles.container}>
<Stack.Navigator screenOptions={{headerShown: false}}>
{/* {loggedIn === false ? ( */}
<Stack.Screen name="/">
{props => <LandingPage {...props} handleLoggedIn={handleLoggedIn} rememberMe={rememberMe} setRememberMe={setRememberMe} />}
</Stack.Screen>
{/* ) : null} */}
<Stack.Screen name="home">
{props => <Home {...props} handleLoggedIn={handleLoggedIn} />}
</Stack.Screen>
{/* About 40 more Stack Screens */}
</Stack.Navigator>
</View>
</KeyboardAvoidingView>
</PaperProvider>
</ApplicationProvider>
</RecoilRoot>
</ApolloProvider>
</NavigationContainer>
</ErrorBoundary>
)
}
catch(error){
return(<ErrorPrompt code={error} />)
}
I apologize for the amount of code, but my primary question is, does the order of the wrapping components matter? I know I need my NavContainer, ErrorBoundary, and RecoilRoot wrapping all of the pages, but do the orders of these matter? Because the code I have about was working perfectly until I added a mutation to the ErrorPrompt Component. The new ErrorPrompt looked like this...
const ErrorPrompt = (props) => {
useEffect(() => {
sendErrorEmail({
variables: {
errorCode: props.error.toString()
}
})
}, [])
return(
// The same return as before
)
}
After this addition, I was given the Could not find "client" in the context or passed in as an option. Wrap the root component in an <ApolloProvider>, or pass an ApolloClient instance error. The message isn't particularly helpful, as both everything is wrapped in AND I have a client passed in. Does anyone know the solution / if order of these wrappings matters, and it it does, what order it should be in?
I found the issue. It was not a matter of the order in which things were wrapped but rather that I had the declaration for the sendErrorEmail mutation OUTSIDE of a component that was wrapped with the Apollo Provider. I had it out floating in App.js, but when I moved the line to under ErrorPrompt = () => { the error was removed

useEffect not working in custom drawer component without refresh

So I am using react-navigation 5 and I have a custom drawer component for my app. I want to display the name of the logged-in user in the drawer for which I am using a state variable and I am updating the state from firestore. I am calling a function in useEffect which accesses firestore and gets the name of the user. But I think the useEffect is not working without refresh because unless I save the project and refresh the application the state is not getting updated in the application and I cannot see the name of the user without refreshing but it is visible after a refresh. Any ideas why this is happening? Any help would be appreciated. Thank you.
Custom drawer
export default function CustomDrawer(props) {
const paperTheme = useTheme();
const [name,setName]=useState('');
useEffect(() => {
doStuff();
}, []);
const doStuff = async () => {
var phone=global.phone;
await firestore().collection("Users").where('Phone Number', '==', phone).get().then(querySnapshot=>{
querySnapshot.forEach(documentSnapshot => {
console.log("in drawer");
console.log(documentSnapshot.data());
setName(documentSnapshot.data().Name);
})
})
};
return(
<View style={{flex:1}}>
<DrawerContentScrollView {...props}>
<View style={styles.drawerContent}>
<View style={styles.userInfoSection}>
<View style={{flexDirection:'row',marginTop: 15}}>
<Avatar.Image
source={{
uri: ''
}}
size={50}
/>
<View style={{marginLeft:15, flexDirection:'column'}}>
<Title style={styles.title}>{name}</Title>
</View>
</View>
</View>
</View>
</DrawerContentScrollView>
</View>
);
}
Looks like you have doStuff function defined outside the useEffects.
Either you need to put it inside useEffects or add it in dependency list
useEffect(() => {
doStuff();
}, [doStuff]);

React-native FlatList component is Not Working

Hello I am a beginner to react-native. I was practicing with a basic app which reads the input from the user and display it in the screen. I am trying to use the FlatList but it is not rendering.
Here is my code
export default function App() {
const [enteredText, setEnteredText] = useState(
""
); /* constant and method with String*/
const [enteredString, setEnteredString] = useState(
[]
); /* constant and method with Array */
const enteredTextHandler = (enteredText) => {
setEnteredText(enteredText);
}; /* Set the entered text to the constant value*/
const addTextHandler = () => {
setEnteredString((currentString) => [
...currentString,
{ id: Math.random().toString(), value: enteredText },
]); /* Add entered text to the array */
};
return (
<View style={styles.mainView}>
<View style={styles.container}>
<TextInput
placeholder="Enter te value"
style={styles.input}
onChangeText={enteredTextHandler}
value={enteredText}
/>
<Button title="Add" onPress={addTextHandler} style={styles.button} />
</View>
<FlatList
data={enteredString}
keyExtractor={(item) => item.id}
renderItem={(itemData) => {
console.log(itemData.item.value);
<View style={styles.listStyle}>
<Text>{itemData.item.value}</Text>
</View>;
}}
/>
</View>
);
}
It might be because you've reused the variable name enteredText in your enteredTextHandler. I think the way it's set up it'll always set enteredText to blank.
Try using a different name for the text argument:
const enteredTextHandler = newText => {
setEnteredText(newText);
}; /* Set the entered text to the constant value*/
I got the answer
<FlatList
data={enteredString}
keyExtractor={(item, index) => item.id}
renderItem={(itemData) => (
<View style={styles.listStyle}>
<Text>{itemData.item.value}</Text>
</View>
)}
/>
I was written the syntax wrong. previously I used (itemData) => {} but the actual syntax is (itemData) => () . Instead of parantesis I used curly braces that's why it was not displaying.

Get user input from input field in react similar to getElementById in react native using props

I am doing a loan calculation app and i run into the trouble since i am new to react native and previously i have been manipulating the DOM using querySelector or getElementById functions. However this does not work in react, and i am using state to store the value from the user, but i just can't seem to get it right, What am i doing wrong?
I've inserted the calculation element that is later rendered in app.js. All elements are showing up with no error, but the problem is to get user input data and then be able to use that data and do calculations.
Here is my Class
class LanKalkylElement extends React.Component {
constructor(props) {
super(props);
this.state = {
loanAmount: 20000,
loanInterest: 2.5,
loanYear: 10,
};
}
changeAmount(loanAmount) {
this.setState(() => {
return {
loanAmount: parseFloat(loanAmount),
};
});
}
changeInterest(loanInterest) {
this.setState(() => {
return {
loanInterest: parseFloat(loanInterest),
};
});
}
changeYear(loanYear) {
this.setState(() => {
return {
loanYear: parseFloat(loanYear),
};
});
}
calcButton() {
Alert.alert(this.props.loanAmount);
}
buttonHomeFunc() {
this.props.navigation.navigate('Start');
}
render() {
const {loanAmount, loanInterest, loanYear} = this.state;
return(
<View style={styles.contentStyle}>
<Text style={styles.text}> Lånebelopp </Text>
<TextInput style={styles.numericInput}
onBlur={Keyboard.dismiss}
keyboardType={'numeric'}
value={loanAmount}
onValueChange={this.changeAmount.bind(this)} />
<Text style={styles.text}> Ränta </Text>
<TextInput style={styles.numericInput}
onBlur={Keyboard.dismiss}
keyboardType={'numeric'}
value={loanInterest}
onValueChange={this.changeInterest.bind(this)} />
<Text style={styles.text}> Antal år: {String(loanYear)}</Text>
<Slider step={1}
maximumValue={15}
value={loanYear}
onValueChange={this.changeYear.bind(this)} />
<Button title='Kalkylera' onPress={() => this.calcButton()}/>
<Text style={styles.textResult}>Total summa att återbetala:</Text>
<Text style={styles.textResult}>varav räntekostnad:</Text>
<Button title='Tillbaka' onPress={() => this.buttonHomeFunc()}/>
</View>
)
}
}
export default withNavigation(LanKalkylElement);
When a user changes a value in a text input, onValueChange is called. You have bound this prop to functions that modify the state for this component.
This means the value in the text input will always match the value in the state. Therefore, if you need to access the value in a text input you would simply retrieve it from the state, like this:
const loanAmount = this.state.loanAmount;
doSomethingWithLoanAmount(loanAmount);

React Native - how to use map function on an object

I'm creating an APP that get some data from fetch function. No problem here. The array has the data correctly. I'm doing it like this:
constructor(){
super()
this.state = {
fetching: false,
api: []
}
}
componentWillMount(){
this.setState({ fetching: true })
api.getMonuments().then(res => {
this.setState({
api: res,
fetching: false
})
})
}
I got this data: an array of 4 objects
Then I want to pass that data to another scene. I'm doing it like this:
<View style={styles.contentContainer}>
<TouchableHighlight
style={[styles.button, {marginBottom:0}]}
onPress={() => navigate('Monumento', this.state.api)}
underlayColor='#000'
>
<View style={styles.buttonContent}>
<Animatable.Text
style={styles.buttonText}
animation="bounceInLeft"
duration={1500}
>
Click here!
</Animatable.Text>
</View>
</TouchableHighlight>
</View>
On the other scene I get that data with the navigation.state.params but the problem now is that there is no more an array with 4 objects in it, but instead there is an object that have 4 objects in it...if I console log the data that is what's appears
render(){
const api = this.props.navigation.state.params;
console.log('API:', api)
...
Now I want to use the map function but I can't because 'api' is not a function...How can I workaround this?
render(){
var api={"bar":"nihao"};
return(
<View>
{Object.entries(api).map(([key,v])=>{
return <View key={key}><Text>{v}</Text></View>
})}
</View>
)
}
api is a single object not array.
api is a array.
render(){
var api=[{"bar":"nihao"},{"bar":"nihao2"},{"bar":"nihao3"}];
return(
<View>
{api.map((v,index)=>{
return <View key={index}><Text>{v.bar}</Text></View>
})}
</View>
)
}
You can use Object.entries with RN for mapping the key/value pairs of an object. Eg:
const api = { 'foo': 'bar', 'foz': 'baz'};
...
render() {
return (
Object.entries(api).map(([key, value]) => {
return <View key={key}>{value}</View>
});
)
}
The issue is you are accessing params object, but what you want is api array. I guess you are using react navigation. If so, then your call to navigate function should be like this:
navigate('Monumento', {api: this.state.api}).
And you can retrieve it like this:
this.props.navigation.state.params.api.
Navigate function takes screen name and params object.
Read this: https://reactnavigation.org/docs/navigators/navigation-prop#navigate-Link-to-other-screens