I'm very new to react so apologies if this question has already been answered or should be phrased differently. I have a functional component that fetches a.json file from my public folder in an async function (loadData()). Using the developer tools in my chrome window, I can see that the function gets me exactly what I want, yet the state doesn't seem to want to update when I use setData.
Edit: So I think I know what the problem is, which is that the first time the component renders, the variable source
needs that JSON object which won't be there until the component re-renders. If that's the case, should all the code starting at let source = pickRandomVerb(data)
go somewhere outside useEffect()?
function ComposedTextField() {
const classes = useStyles();
const [data, setData] = React.useState([]);
const [displayVerb, setDisplayVerb] = React.useState('');
const pickRandomVerb = (list) => {
var obj_keys = Object.keys(list);
var ran_key = obj_keys[Math.floor(Math.random() * obj_keys.length)];
return list[ran_key];
}
const loadData = async() => {
const response = await fetch('verbs.json');
const json = await response.json();
setData(json);
console.log(json); //json is exactly what I want here
console.log(data); //data is just '[]' here
}
useEffect(() => {
loadData();
console.log(data) //data is also '[]' here
let source = pickRandomVerb(data)
let verbSource = source.infinitive;
let showVerb = verbSource.toString().replaceAll("\"", "");
setDisplayVerb(showVerb)
}, [])
return(
<div>
<Typography className = {classes.form}>
<p>{displayVerb}</p>
</Typography>
</div>
)
}
Can anyone let me know what I'm missing? Thanks in advance.
setState is async also then you can't see the data changed in
const loadData = async() => {
const response = await fetch('verbs.json');
const json = await response.json();
setData(json);
console.log(json); //json is exactly what I want here
console.log(data); //data is just '[]' here
}
Your useEffect doesn't define the dependency then it just run once. If you want to see your data changed just add dependency for data to track when data changed.
/// Just load at the first time you render you component
useEffect(()=>{
loadData();
},[])
useEffect(() => {
console.log(data)
let source = pickRandomVerb(data)
let verbSource = source.infinitive;
let showVerb = verbSource.toString().replaceAll("\"", "");
setDisplayVerb(showVerb)
}, [data]) //Just add dependency here for useEffect
useState hook is also asynchronous, and will not be reflected immediately and this question already addressed link-1 , link-2
changes required (you have to listen the changes of the data in useEffect),
useEffect(() => {
loadData();
console.log(data) //data is also '[]' here
}, []);
useEffect(() => {
let source = pickRandomVerb(data)
let verbSource = source.infinitive;
let showVerb = verbSource.toString().replaceAll("\"", "");
setDisplayVerb(showVerb)
}, [data]);
Maybe you can try something like this
function ComposedTextField() {
const classes = useStyles();
const [data, setData] = React.useState([]);
const [displayVerb, setDisplayVerb] = React.useState('');
const pickRandomVerb = (list) => {
var obj_keys = Object.keys(list);
var ran_key = obj_keys[Math.floor(Math.random() * obj_keys.length)];
return list[ran_key];
}
const loadData = async() => {
const response = await fetch('verbs.json');
const json = await response.json();
setData(json);
console.log(json); //json is exactly what I want here
console.log(data); //data is just '[]' here
}
useEffect(() => {
loadData();
console.log(data) //data is also '[]' here
}, [])
// Move that code outside useEffect and inside condition
if(data!==[]){
let source = pickRandomVerb(data)
let verbSource = source.infinitive;
let showVerb = verbSource.toString().replaceAll("\"", "");
setDisplayVerb(showVerb)
}
return(
<div>
<Typography className = {classes.form}>
<p>{displayVerb}</p>
</Typography>
</div>
)
}
In this code setDisplayVerb() will be called when the data has some value. The flow of the control would be something like this.
This way you should be able to see the data on your screen.
so to expand on my comment, try this:
const loadData = async() => {
const response = await fetch('verbs.json');
const json = await response.json();
return json; /// <<< note: here you return data
}
useEffect(async () => {
const data = await loadData(); // <<<< note: here you wait for data
console.log(data);
let source = pickRandomVerb(data)
let verbSource = source.infinitive;
let showVerb = verbSource.toString().replaceAll("\"", "");
setDisplayVerb(showVerb)
}, [])
this will only get you the data on initial load.
Just as a side note: to improve a bit on this component, you can move your loadData
function outside of the component.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.