繁体   English   中英

每次我发送新的 state 时,组件都会意外地重新安装。我将 ContextProvider 与 useReducer 一起使用

[英]component unexpectedly re-mounts every time i dispatch new state. Im using ContextProvider with useReducer

我使用 Context.Provider + useReducer,我的上下文中有 function“fetchCars()”用于获取汽车,这取决于所选的过滤器值

可能是新手问题,但我不明白为什么每次更改过滤器值后都会安装名为“Filters.jsx”的消费者组件。 因此,我无法在 Filter.jsx 组件的 useState 中保存值

Codesandbox 链接https://codesandbox.io/s/peaceful-morse-21zj6m?file=/src/components/Filters.jsx

在 Codesandbox 中,您可以在过滤器更改时看到控制台打印

CarsContextProvider.jsx

import { useReducer, createContext, useCallback } from "react";

export const CarsContext = createContext()

const getCarsFromServer = (status) => {
    // dummy fetch
    const dataFromServer = [
        { id: 1, name: 'Volvo', status: 'notAvailable' },
        { id: 2, name: 'BMW', status: 'inStock' },
        { id: 3, name: 'Mercedes', status: 'notAvailable' },
        { id: 4, name: 'Audi', status: 'notAvailable' },
        { id: 5, name: 'Opel', status: 'inStock' },
        { id: 6, name: 'Renault', status: 'inStock' },
    ]

    return new Promise((resolve) => {
        setTimeout(() => {
            if (status === 'all') {
                return resolve(dataFromServer)
            }
            resolve(dataFromServer.filter(item => item.status === status))
        }, 500);
    })
}

const reducer = (state, action) => {

    switch (action.type) {
        case 'pending':
            return { ...state, loading: true }
        case 'success':
            return { ...state, loading: false, items: action.payload }
        case 'error':
            return { ...state, loading: false, error: action.payload }
        case 'setFilter':
            return { ...state, statusFilter: action.payload }
        default:
            break;
    }
}

const initState = {
    items: [],
    loading: false,
    error: '',
    statusFilter: 'all',
}

const CarsContextProvider = ({ children }) => {

    const [state, dispatch] = useReducer(reducer, initState)

    const fetchCars = useCallback(async () => {

        try {
            dispatch({ type: 'pending' })
            const data = await getCarsFromServer(state.statusFilter)
            dispatch({ type: 'success', payload: data })
        } catch (error) {
            dispatch({ type: 'error', payload: error })
        }
    }, [state.statusFilter])

    return (
        <CarsContext.Provider value={{ state, dispatch, fetchCars }}>
            {children}
        </CarsContext.Provider>
    )
}

export default CarsContextProvider

应用程序.jsx

import CarsScreen from "./components/CarsScreen";
import CarsContextProvider from "./context/CarsContext";

function App() {
  return (
    <CarsContextProvider>
      <CarsScreen />
    </CarsContextProvider>
  );
}

export default App;

CarsScreen.jsx

import CarsList from "./CarsList"

const CarsScreen = () => {

    return (
        <div>
            <CarsList />
        </div>

    )
}

export default CarsScreen

汽车列表.jsx

import { useContext, useEffect } from "react"
import { CarsContext } from "../context/CarsContext"
import Filters from "./Filters"

const CarsList = () => {

    const { state, fetchCars } = useContext(CarsContext)

    useEffect(() => {

        fetchCars()

    }, [fetchCars])

    if (state.loading) return <h3>loading...</h3>

    return (
        <>
            <Filters />
            <hr />
            <ul>
                {state.items.map((car => <li key={car.id}>{car.name}</li>))}
            </ul>
        </>
    )
}

export default CarsList

过滤器.jsx

import { useState, useEffect, useContext } from "react"
import { CarsContext } from "../context/CarsContext"

const Filters = () => {

  const [localState, setLocalState] = useState('init')
  const { state, dispatch } = useContext(CarsContext)

  useEffect(() => {
    // There is my question! Why console.log executing every time i change filter select option ?
    console.log('component mounted');
  }, [])

  const filterChangeHandler = (e) => {

    //and also localState could not change, because this component every time mounts with init value 
    setLocalState('filter changed')

    // this dispatch changes filter value, and items fetching from server  
    dispatch({ type: 'setFilter', payload: e.target.value })
  }

  return (
    <div>
      <select
        name="stockFilter"
        onChange={filterChangeHandler}
        defaultValue={state.statusFilter}
      >
        <option value="all">show all</option>
        <option value="inStock">in stock</option>
        <option value="notAvailable">not available</option>
      </select>
      <p>Filters local state is : {localState}</p>
    </div>
  )
}

export default Filters

Codesandbox 链接https://codesandbox.io/s/peaceful-morse-21zj6m?file=/src/components/Filters.jsx

试图在 index.js. 中评论 React.StrictMode 行,但没有效果是否可以避免这种不需要的挂载 Filters.jsx 组件?

发现问题。 问题出在组件 GoodsList.jsx 加载状态正在卸载 Filter.jsx

if (state.loading) return <h3>loading...</h3> 

GoodsList.jsx 的固定版本

import { useContext, useEffect } from "react"
import { GoodsContext } from "../context/GoodsContextProvider"
import GoodsFilter from "./GoodsFilter"

const GoodsList = () => {

    const { state, fetchGoods } = useContext(GoodsContext)

    useEffect(() => {

        fetchGoods()

    }, [fetchGoods])


    return (
        <>
            <GoodsFilter />
            {state.loading ? <h3>loading...</h3>
                :
                <>
                    <hr />
                    <ul>
                        {state.items.map((good => <li key={good.id}>{good.name} - {good.quantity}</li>))}
                    </ul>
                </>
            }

        </>
    )
}

export default GoodsList

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM