我有一個 React 組件,它接受一個 prop 并通過useState
. 當該道具發生變化時,我有時需要更新狀態作為回應,所以我添加了一個useEffect
. 但是 props 更改會導致渲染,在渲染useEffect
之后觸發,設定 state 會導致另一個render
,并且在這兩個渲染之間,我的組件處于非法狀態,我不知道如何防止它。
這是一個簡單的例子。該組件顯示一個單選按鈕串列,每個按鈕代表一個城市。一次僅啟用特定國家/地區內城市的單選按鈕。當國家/地區發生變化時,它會更新啟用了哪些單選按鈕,并且還會將用戶的選擇更改為有效的城市。
import { useEffect, useState } from 'react';
const CITIES_BY_COUNTRY = {
Spain: ['Madrid', 'Barcelona', 'Valencia'],
France: ['Paris', 'Lyon', 'Marseille'],
};
export function CityPicker({ currentCountry }) {
const [selectedCity, setSelectedCity] = useState('');
// When the country changes, make sure the selected city is valid.
useEffect(() => {
if (!CITIES_BY_COUNTRY[currentCountry].includes(selectedCity)) {
setSelectedCity(CITIES_BY_COUNTRY[currentCountry][0]);
}
}, [currentCountry, selectedCity]);
// Log the country/city pair.
console.log({ currentCountry, selectedCity });
return (
<div>
{Object.keys(CITIES_BY_COUNTRY).map(country => (
<div key={`country-${country}`}>
{Object.keys(CITIES_BY_COUNTRY[country]).map(city => (
<label key={`city-${city}`}>
<input
type="radio"
name="city"
value={city}
disabled={country !== currentCountry}
checked={city === selectedCity}
onChange={() => setSelectedCity(city)}
/>
{city}
</label>
))}
</div>
))}
</div>
);
}
用戶帶著 currentCountry === "Spain" 到達。僅啟用西班牙城市。日志說
{ currentCountry: "Spain", selectedCity: "Madrid" }
用戶點擊“巴塞羅那”。日志說
{ currentCountry: "Spain", selectedCity: "Barcelona" }
。到目前為止一切都很好。父組件中的某些內容發生了
currentCountry
變化,而法國也發生了變化。該組件通過新的道具并重新渲染。日志說{ currentCountry: "France", selectedCity: "Barcelona" }
。然后,useEffect
火災,我們得到另一個渲染。日志說{ currentCountry: "France", selectedCity: "Paris" }
。
如您所見,我們在第 3 步中得到了兩個渲染,其中一個有一個非法配對(法國 巴塞羅那)。
這是一個簡單的例子,我的應用程式要復雜得多。國家和城市都可以通過多種方式進行更改,我需要每次都對這對進行驗證,有時會提示用戶或在某些情況下做出反應。鑒于此,防止非法配對非常重要。
鑒于useEffect
只有在渲染后才會觸發,所以做出我需要的更改似乎總是為時已晚。有沒有優雅的方法來解決這個問題?
uj5u.com熱心網友回復:
useMemo
在這種情況下作業,同時防止額外的渲染。為了防止您描述的此類問題,您可以將國家和城市選擇都設為受控組件并從父級管理它們的狀態(將值和處理程式作為道具傳遞)。
import { useMemo, useState } from 'react';
const CITIES_BY_COUNTRY = {
Spain: ['Madrid', 'Barcelona', 'Valencia'],
France: ['Paris', 'Lyon', 'Marseille'],
};
export function CityPicker({ currentCountry }) {
const [selectedCity, setSelectedCity] = useState('');
const currentCity = useMemo(() => {
const defaultCity = CITIES_BY_COUNTRY[currentCountry][0];
const isCityValid = CITIES_BY_COUNTRY[currentCountry].includes(selectedCity);
return isCityValid ? selectedCity : defaultCity;
}, [selectedCity, currentCountry]);
return (
<div>
{Object.keys(CITIES_BY_COUNTRY).map(country => (
<div key={`country-${country}`}>
{Object.keys(CITIES_BY_COUNTRY[country]).map(city => (
<label key={`city-${city}`}>
<input
type="radio"
name="city"
value={city}
disabled={country !== currentCountry}
checked={city === currentCity}
onChange={() => setSelectedCity(city)}
/>
{city}
</label>
))}
</div>
))}
</div>
);
}
uj5u.com熱心網友回復:
這是我對解決方案的最佳嘗試,盡管我覺得它并不優雅。我想知道其中一些是否可以以某種方式抽象為自定義鉤子。
export function EffectiveCityPicker({ currentCountry }) {
const [selectedCity, setSelectedCity] = useState(CITIES_BY_COUNTRY[currentCountry][0]);
// Based on the country, make sure we have a valid city.
let effectiveSelectedCity = selectedCity;
if (!CITIES_BY_COUNTRY[currentCountry].includes(selectedCity)) {
effectiveSelectedCity = CITIES_BY_COUNTRY[currentCountry][0];
}
// If the effectiveSelectedCity changes, save it back to state.
useEffect(() => {
if (selectedCity !== effectiveSelectedCity) {
setSelectedCity(effectiveSelectedCity);
}
}, [selectedCity, effectiveSelectedCity]);
// Log the country/city pair.
console.log({ currentCountry, effectiveSelectedCity });
return (
<div>
{Object.keys(CITIES_BY_COUNTRY).map(country => (
<div key={`country-${country}`}>
{Object.keys(CITIES_BY_COUNTRY[country]).map(city => (
<label key={`city-${city}`}>
<input
type="radio"
name="city"
value={city}
disabled={country !== currentCountry}
checked={city === effectiveSelectedCity}
onChange={() => setSelectedCity(city)}
/>
{city}
</label>
))}
</div>
))}
</div>
);
}
在這個版本中,我們總是將當前的 props 和 state 組合成一個 valid effectiveSelectedCity
,然后在需要時將其保存回 state。它可以防止非法對,但會導致產生相同標記的額外渲染。如果有更多的狀態和道具,它也會變得更加復雜——也許有一個使用減速器的類似解決方案。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/468923.html
標籤:javascript 反应
上一篇:如果HTML元素在函式內更新,則ReactSetState直到第二次渲染才起作用
下一篇:基于回應ReactJS渲染組件