Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React update state with button click using useState

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>

}
like image 894
Eric Jansen Avatar asked Jun 14 '26 12:06

Eric Jansen


1 Answers

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.

like image 127
Shubham Khatri Avatar answered Jun 17 '26 22:06

Shubham Khatri



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!