'How to await state change in React Native?
I have spent the whole day searching through similar questions etc. but to no avail and I am losing my mind.
I am building a "translate app" using an API. In my Home functional component which is one of three in my bottom tab navigator, I have these states:
const [from, setFrom] = useState({language:'German', code:'ge_GE'});
const [to, setTo] = useState({language: 'English', code: 'en_GB'});
let [input, setInput] = useState("");
const [output, setOutput] = useState("");
const [favorites, setFavorites] = useState([])
There's a <TextInput> with onChange handler to setInput.
I call fetch() as follows:
const fetch = () => {
axios.request(options).then(function (response) {
setOutput(response.data.result);
}).catch(function (error) {
console.error(error);
setOutput("");
});
}
const options = {
method: 'POST',
url: 'https://lingvanex-translate.p.rapidapi.com/translate',
headers: {
'content-type': 'application/json',
'X-RapidAPI-Host': 'lingvanex-translate.p.rapidapi.com',
'X-RapidAPI-Key': 'xxxx-xxxx-xxxx-xxxx'
},
data: '{"from":"'+ from.code +'","to":"'+ to.code +'","data":"'+ input +'","platform":"api"}'
};
The output then updates in my UI and the wanted result in UI is achieved.
THE PROBLEM IS: If I then use a button to save this translation to favorites with saveHandler on onPress prop like this:
function saveHandler(){
const newFaves = [...favorites,{phrase: input, translation: output, source: from.code, target: to.code, key: favorites.length + 1 }];
setFavorites(newFaves);
}
After inspecting the latest favorite state:
useEffect(() => {
console.log(favorites);
},[favorites])
I see that all the states have held their previous values while being supplied in saveHandler.
I have seen similar issues on SO, people using second callback arguments, but I keep getting told by expo console that useState does not support it. I am not really trying to turn to class components as I am absolutely lost after trying it. I believe there must be a way to do this. I ask you kind souls of SO, for your helping hand :(
edit: I understand such thing is achievable through useEffect, but I did not know how can I use this useEffect to "update its own dependency"... I think I figured out a way but it seems it might cause performance issues. After creating a non-state variable counterpart to every state, I can now do
useEffect(() => {
hardOutput = output
},[output])
with saveHandler using the assigned non-state variable instead of state itself. Since I need to achieve this for four states, this seems to be lagging up the app, but is working somehow.
function saveHandler(){
const newFaves = [...favorites,{phrase: hardInput, translation: hardOutput, source: hardFrom.code, target: hardTo.code, key: favorites.length + 1 }];
setFavorites(newFaves);
}
edit 2: I found this article on stale closures https://dmitripavlutin.com/react-hooks-stale-closures/ and tried using the functional setState((previousState) => previousState = newState) but still got the same results.
REPREX:
import { useState, useEffect} from 'react';
import { View, Text, Button, TextInput} from 'react-native';
const axios = require("axios");
function Home() {
const [from, setFrom] = useState({language:'Slovenčina', code:'sk_SK', key: 1});
const [to, setTo] = useState({language: 'Angličtina', code: 'en_GB', key: 2});
let [input, setInput] = useState("");
const [output, setOutput] = useState('')
const [favorites, setFavorites] = useState([{
phrase: "Hello",
translation: "Dobrý deň",
source: "en_EN",
target: "sk_SK",
key: "1"
}]);
const fetch = () => {
axios.request(options).then(function (response) {
setOutput(response.data.result);
}).catch(function (error) {
console.error(error);
setOutput("");
});
}
let saveButton = [];
const options = {
method: 'POST',
url: 'https://lingvanex-translate.p.rapidapi.com/translate',
headers: {
'content-type': 'application/json',
'X-RapidAPI-Host': 'lingvanex-translate.p.rapidapi.com',
'X-RapidAPI-Key': 'xxxx-xxxx-xxxx-xxxx'
},
data: '{"from":"'+ from.code +'","to":"'+ to.code +'","data":"'+ input +'","platform":"api"}'
};
function inputHandler(newText) {
setInput(newText);
setOutput("");
}
useEffect(() => {
saveButtonHandler()
},[output])
function saveButtonHandler() {
saveButton= [];
saveButton.push(<TouchableOpacity onPress={saveHandler}>
<Text>Save</Text>
</TouchableOpacity>);
}
function saveHandler(){
const newFaves = [...favorites,{phrase: input, translation: output, source: from.code, target: to.code, key: favorites.length + 1 }];
setFavorites(newFaves);
}
useEffect(() => {
console.log(favorites);
},[favorites])
return (
<TextInput
value={input}
onChangeText={inputHandler}/>
<Button title="Translate" onPress={translateHandler}>
{saveButton}
);
}
Note: Functional component above is nested inside a tab navigation component nested inside main App function in App.js
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
