@vovashaplin

Изменения стейта опаздывают (HOOKS + CONTEXT)?

После нажатия на кнопку в Paginator срабатывает функция pageChanged. Она корректно передает значение currentPage из компонента Pagination. После чего выполняется функция fetchItems, которая должна выловить с апи нужные записи, но она вылавливает предыдущие записи, потому что стейт не успел почему то измениться. После повторного нажатия на кнопку уже показываются корректные данные.
Вот компонент
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>
    )
}

Вот ItemsContext
import { createContext } from 'react'
export const ItemsContext = createContext()

Вот 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)
}

Вот 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>
    )
}
  • Вопрос задан
  • 80 просмотров
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы