Reputation: 8408
I have tried many things and can't seem to understand why setTypes won't update the 'types' array??
import { useState, useEffect } from 'react';
import { PostList } from './post-list';
import * as api from '../utils/api';
export const PostSelector = (props) => {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(false);
const [type, setType] = useState('post');
const [types, setTypes] = useState([]);
const fetchTypes = async () => {
setLoading(true);
const response = await api.getPostTypes();
delete response.data.attachment;
delete response.data.wp_block;
const postTypes = response.data;
console.log(response.data); // {post: {…}, page: {…}, case: {…}}
setTypes(postTypes);
console.log(types); // []
// Why types remain empty??
}
const loadPosts = async (args = {}) => {
const defaultArgs = { per_page: 10, type };
const requestArgs = { ...defaultArgs, ...args };
requestArgs.restBase = types[requestArgs.type].rest_base; // Cannot read property 'rest_base' of undefined
const response = await api.getPosts(requestArgs);
console.log(response.data);
}
useEffect(() => {
fetchTypes();
loadPosts();
}, []);
return (
<div className="filter">
<label htmlFor="options">Post Type: </label>
<select name="options" id="options">
{ types.length < 1 ? (<option value="">loading</option>) : Object.keys(types).map((key, index) => <option key={ index } value={ key }>{ types[key].name }</option> ) }
</select>
</div>
);
}
Please, take a look at the console.log and notice the different responses.
What I am trying to do is to load list of types, in this case 'post', 'page' and 'case' and then render a list of posts based on the current 'type'. The default type is 'post'.
If I add [types] to useEffect. I finally get the values but the component renders nonstop.
Thanks to everyone for your comments. Multiple people have pointed out the problem, being that, the fact that we set the state doesn't mean it will set right away because it it asynchronous.
How do we solve this problem then? Regardless of the reasons, how do we get it done? How do we work with our state at any point in time and perform calculations based on our state if we don't know when it will become available? How do we make sure we wait whatever we need to and then use the values we expect?
Upvotes: 1
Views: 11090
Reputation: 3114
For any one coming here and not being able to set/update a useState array you need to use a spread operator (...) and not just the array e.g. "[...initState]" instead of "initState" ... in Typescript
//initialise
const initState: boolean[] = new Array(data.length).fill(false);
const [showTable, setShowTable] = useState<boolean[]>([...initState]);
// called from an onclick to update
const updateArray = (index: number) => {
showTable[index] = !showTable[index];
setShowTable([...showTable]);
};
Upvotes: 1
Reputation: 14198
It not due to asynchronous
as other answers said !!!
You can check the changes by console.log
at return
function like this.
return (
<div> Hello World!
{
console.log(value) // this will reference every re-render
}
</div>
);
or create a new one useEffect with value
as a dependency like below
React.useEffect(() => {
console.log(value); // this will reference every value is changed
}, [value]);
function App() {
const [value, Setvalue] = React.useState([]);
React.useEffect(() => {
Setvalue([1, 2, 3]);
console.log(value); // this will reference to value at first time
}, []);
return (
<div> Hello World!
{
console.log(value) // this will reference every re-render
}
</div>
);
}
ReactDOM.render(<App />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="app"></div>
Read here in more detail: useState set method not reflecting change immediately
Upvotes: 0
Reputation: 625
You have declared your types to be an array, yet you are passing a dictionary of dictionaries through to it. Try this:
const [types, setTypes] = useState({});
You also do not need to call
loadPosts()
becuase the useState hook will re-render your component, only updating what is needed.
Upvotes: 0
Reputation: 29
useState
's setTypes is an asynchronous function so the changes do not take effect immediately. You can use useEffect
to check if anything changes
useEffect(()=>{
const defaultArgs = { per_page: 10, type };
const requestArgs = { ...defaultArgs, ...args };
requestArgs.restBase = types;
console.log("types updated",types)
},[types])
You can remove loadPosts
because now useEffect will run whenever types change
Upvotes: 0
Reputation: 73
It seems like useState is asynchronous and does not update the value instantly after calling it.
Upvotes: 0