
[英]React useReducer state update with blank data and then new data within ContextProvider after dispatch ( causing 2 renders )
[英]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.