Reputation: 440
Can anyone explain to me what the "right" way is to do the below problem. I have this simple page which has event-data. Initially this is data from the last week, but there is a button on the page which sets data to all. I use useEffect() to loadData() with setting all=false initially. When I click the button, I want to update all to true and data to all data.
The below code works, but it is somewhat weird, because I first have to get the data for !all and after that I set all=true, but then it rerenders again.
Is there a nice way to do this? I tried putting the "onClick" code the other way around, so: setAll(!all);loadData(all), but this doesnt work, because the state isnt immediately updated.
Any help/tips would be great!!
const Events = ({auth}) => {
const [data, setEventData] = useState([])
const [all, setAll] = useState(false)
const loadData = async (givenScope) => {
const scope = (givenScope ? '' : '?scope=last_week')
const eventdata = await makeAPICall('/api/events' + scope, 'GET', null, await auth.getAccessToken())
setEventData(eventdata);
}
useEffect(() => {
loadData(all);
}, [])
const columns = [{
Header: 'Datum/tijd',
accessor: 'datetime',
Cell: props => <Moment date={props.value} tz="Europe/Amsterdam" format="YYYY-MM-DD HH:mm"/>
}, {
Header: 'Event',
accessor: 'value',
}]
return <div><Button onClick={() => {loadData(!all);setAll(!all)}}>{all ? 'Last week' : 'All'}</Button> <DefaultTable data={data} columns={columns} loading={data.length > 0 ? false : true} /></div>
}
Upvotes: 3
Views: 15680
Reputation: 31495
You can try something like this on your button's onClick
:
<Button
onClick={ () => {
setAll((prevState) => {
loadData(!prevState); // This will load the data during the state update
return !prevState;
}
}}
/>
Upvotes: 0
Reputation: 282050
A relatively simple way to handle the above code would be pass all
as value to dependency array of useEffect
so that you don't worry about loadData
being executed after state update
const Events = ({auth}) => {
const [data, setEventData] = useState([])
const [all, setAll] = useState(false)
const loadData = async (givenScope) => {
const scope = (givenScope ? '' : '?scope=last_week')
const eventdata = await makeAPICall('/api/events' + scope, 'GET', null, await auth.getAccessToken())
setEventData(eventdata);
}
useEffect(() => {
loadData(all);
}, [all])
const columns = [{
Header: 'Datum/tijd',
accessor: 'datetime',
Cell: props => <Moment date={props.value} tz="Europe/Amsterdam" format="YYYY-MM-DD HH:mm"/>
}, {
Header: 'Event',
accessor: 'value',
}]
return <div><Button onClick={() => {setAll(all => !all)}}>{all ? 'Last week' : 'All'}</Button> <DefaultTable data={data} columns={columns} loading={data.length > 0 ? false : true} /></div>
}
And in any case your code will render twice since data is available asynchronously. To improve user experience you can show a loader while the data is being fetched.
Upvotes: 5