V
V
vovashaplin2020-05-26 22:28:51
React
vovashaplin, 2020-05-26 22:28:51

Are state changes late (HOOKS + CONTEXT)?

After clicking on the button in Paginator, the pageChanged function is triggered. It correctly passes the currentPage value from the Pagination component. After that, the fetchItems function is executed, which should catch the necessary records from the api, but it catches the previous records, because the state did not have time to change for some reason. After pressing the button again, the correct data is already displayed.
Here is the component

import React, { useEffect, useContext } from 'react'
import Loader from '../../components/UI/Loader/Loader'
import { Paginator } from '../../components/UI/Paginator/Paginator'
import { Layout } from '../Layout/Layout'
import { Card } from '../../components/Card/Card'
import { Alert } from '../../components/Alert/Alert'
import { ItemsContext } from '../../context/items/ItemsContext'

export const Items = () => {

    const items = useContext(ItemsContext)

    useEffect(() => {
        items.fetchItems(true)
    }, [])

    const pageChanged = (index) => {
        items.setCurrentPage(index)
        items.fetchItems(false)
        console.log('Items',index)
        console.log('State',items.currentPage)
    }

    const renderCards = () => {
        return items.items.map((item, index) => {
            return (
                <Card
                    key={item.id}
                    item={item}
                    // addItemToBasket={this.props.addItemToBasket}
                    id={index}
                />
            )
        })
    }

    return (
        <Layout>
            <Paginator
                totalItemsCount={items.totalItemsCount}
                pageSize={items.pageSize}
                currentPage={items.currentPage}
                pageChanged={pageChanged}
                portionSize={3}
            />
            
            <div className="card-group row">
                {
                    items.loading || items.items.length === 0
                        ? <Loader />
                        : renderCards()
                }
            </div>
            <Alert />
        </Layout>
    )
}

Here is the ItemsContext
import { createContext } from 'react'
export const ItemsContext = createContext()

Here is the ItemsReducer
import {
    SET_LOADING,
    FETCH_ITEMS_SUCCESS,
    FETCH_ERROR,
    SET_CURRENT_PAGE,
    SET_TOTAL_COUNT,
    FETCH_ITEM_BY_ID_SUCCESS
} from '../types'

const handlers = {
    [SET_LOADING]: state => ({...state, loading: true}),
    [FETCH_ITEMS_SUCCESS]: (state, {payload}) => ({...state, items: payload, loading: false}),
    [FETCH_ITEM_BY_ID_SUCCESS]: (state, {payload}) => ({...state, item: payload, loading: false}),
    [FETCH_ERROR]: (state, {payload}) => ({...state, error: payload, loading: false}),
    [SET_CURRENT_PAGE]: (state, {payload}) => ({...state, currentPage: payload}),
    [SET_TOTAL_COUNT]: (state, {payload}) => ({...state, totalItemsCount: payload}),
    DEFAULT: state => state
} 

export const ItemsReducer = (state, action) => {
    const handler = handlers[action.type] || handlers.DEFAULT
    return handler(state, action)
}

Here is the ItemsState
import React, { useReducer } from 'react'
import axios from 'axios'
import { ItemsContext } from './ItemsContext'
import { ItemsReducer } from './ItemsReducer'

import {
    SET_LOADING,
    FETCH_ITEMS_SUCCESS,
    FETCH_ERROR,
    SET_CURRENT_PAGE,
    SET_TOTAL_COUNT,
    FETCH_ITEM_BY_ID_SUCCESS
} from '../types'

export const ItemsState = ({ children }) => {
    const initialState = {
        items: [],
        item: null,
        pageSize: 3,
        currentPage: 1,
        totalItemsCount: 0,
        loading: false,
        error: null
        // basket: []
    }

    const [state, dispatch] = useReducer(ItemsReducer, initialState)

    const fetchItems = async bool => {
        setLoading()
        try {
            console.log('ItemsState', state.currentPage)
            const response = await axios.get(`https://reqres.in/api/users?page=${state.currentPage}&per_page=${state.pageSize}`)
            fetchItemsSuccess(response.data.data)
            if (bool) {
                setTotalCount(response.data.total)
            }

        } catch (e) {
            fetchError(e)
        }
    }

    const fetchItemById = async id => {
        setLoading()
        try {
            const response = await axios.get(`https://reqres.in/api/users/${id}`)
            fetchItemByIdSuccess(response.data.data)
        } catch (e) {
            fetchError(e)
        }
    }

    const fetchItemByIdSuccess = (item) => dispatch({ type: FETCH_ITEM_BY_ID_SUCCESS, payload: item })

    const fetchItemsSuccess = (items) => dispatch({ type: FETCH_ITEMS_SUCCESS, payload: items })

    const fetchError = (e) => dispatch({ type: FETCH_ERROR, payload: e })

    const setCurrentPage = (currentPage) => {
        
        dispatch({ type: SET_CURRENT_PAGE, payload: currentPage })
        console.log('setCurrentPage', currentPage)
    }

    const setLoading = () => dispatch({ type: SET_LOADING })

    const setTotalCount = (totalItemsCount) => dispatch({ type: SET_TOTAL_COUNT, payload: totalItemsCount })

    const { items, item, pageSize, currentPage, totalItemsCount, loading } = state

    return (
        <ItemsContext.Provider value={{
            fetchItems,
            fetchItemById,
            setCurrentPage,
            setTotalCount,
            items, item, pageSize, currentPage, totalItemsCount, loading
        }}>
            {children}
        </ItemsContext.Provider>
    )
}

Answer the question

In order to leave comments, you need to log in

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question