[英]Why React component doesn't re-render (change styles) when props changed?
我制作了一個TagItem
JSXElement,當用戶點擊/觸摸時顏色會發生變化。
它接收一個 prop isSelected
,並且在內部selected
了自己的 state ,初始值為 prop isSelected
。 它的樣式會根據selected
的 state 而改變,但是當 prop isSelected
值改變時,樣式不會改變......
我想出的原因是它的 state 即使 prop isSelected
改變也不會改變。
我不知道為什么會這樣。 有人知道嗎?
代碼是這樣的。
type TagItemProps = {
tag: Tag;
isSelected: boolean;
onSelect: (tag: Tag) => boolean;
onDeselect: (tag: Tag) => void;
};
const TagItem = ({ tag, isSelected = false, onSelect, onDeselect }: TagItemProps) => {
const [selected, setSelected] = useState(isSelected);
console.log("isSelected: ", isSelected);
console.log("selected: ", selected);
const handleSelect = useCallback(() => {
if (!onSelect(tag)) return;
setSelected(true);
}, [onSelect, tag]);
const handleDeselect = useCallback(() => {
onDeselect(tag);
setSelected(false);
}, [onDeselect, tag]);
return (
<TouchableOpacity
style={[styles.container, selected ? styles.selected : null]}
onPress={selected ? handleDeselect : handleSelect}
>
<Text style={[styles.text, selected ? { color: '#fff' } : null]}>
{capitalizeFirstLetter(tag.name)}
</Text>
</TouchableOpacity>
);
};
export default TagItem;
const styles = StyleSheet.create({
container: {
flex: 0,
backgroundColor: '#fff',
borderRadius: 20,
paddingHorizontal: 10,
paddingVertical: 12,
margin: 5,
},
selected: {
backgroundColor: 'green',
},
text: {
textAlign: 'center',
fontWeight: 'bold',
},
});
從上面可以看出,當 state selected
為 true 時,應應用styles.selected
。
我通過登錄控制台確認 prop isSelected
更改但未selected
state。
console.log("isSelected: ", isSelected); // false
console.log("selected: ", selected);
有誰知道為什么會這樣?
這是將道具復制到本地 state 的經典案例——您通常不想這樣做,因為它會引入錯誤的表面積,包括您所看到的錯誤。 如果某些東西可用作道具 - 將其復制到本地 state 的目的是什么? 相反,您應該使用回調來更改該道具的 state 存在於祖先中的任何位置。 復制意味着您現在必須管理保持本地 state 和 prop 同步——這就是為什么通常首先復制是一種反模式。
state 在isSelected
更改時不更新的原因是因為useState
的參數只是它的初始值。 根據設計,即使由於道具更改而發生重新渲染,state 項目也不會更新。 將其復制到本地 state 意味着您可以讓它們保持同步(常見的錯誤原因)。
兩種選擇:
選項 A
不要將道具復制到 state 中,因此您甚至不需要確保道具和內部 state 同步。 直接使用isSelected
,去掉state項。 要設置選定的 state,您需要從父組件向下傳遞到組件的 props 回調——它接受更改的值並更改該父組件中的 state。 這消除了道具和您正在渲染的實際事物之間的毫無意義的障礙。
選項 B
如果您出於某種原因必須保留 state 的副本,請確保在道具更改時更新 state 並附加效果。
type TagItemProps = {
tag: Tag;
isSelected: boolean;
onSelect: (tag: Tag) => boolean;
onDeselect: (tag: Tag) => void;
};
const TagItem = ({ tag, isSelected = false, onSelect, onDeselect }: TagItemProps) => {
const [selected, setSelected] = useState(isSelected);
useEffect(() => {
setSelected(isSelected )
}, [isSelected ])
console.log("isSelected: ", isSelected);
console.log("selected: ", selected);
const handleSelect = useCallback(() => {
if (!onSelect(tag)) return;
setSelected(true);
}, [onSelect, tag]);
const handleDeselect = useCallback(() => {
onDeselect(tag);
setSelected(false);
}, [onDeselect, tag]);
return (
<TouchableOpacity
style={[styles.container, selected ? styles.selected : null]}
onPress={selected ? handleDeselect : handleSelect}
>
<Text style={[styles.text, selected ? { color: '#fff' } : null]}>
{capitalizeFirstLetter(tag.name)}
</Text>
</TouchableOpacity>
);
};
export default TagItem;
const styles = StyleSheet.create({
container: {
flex: 0,
backgroundColor: '#fff',
borderRadius: 20,
paddingHorizontal: 10,
paddingVertical: 12,
margin: 5,
},
selected: {
backgroundColor: 'green',
},
text: {
textAlign: 'center',
fontWeight: 'bold',
},
});
只留下我最終得到的代碼(@Adam 的選項 A),它工作得很好。
type TagItemProps = {
tag: Tag;
isSelected: boolean;
onSelect?: (tag: Tag) => boolean;
onDeselect?: (tag: Tag) => void;
};
const TagItem = ({ tag, isSelected, onSelect, onDeselect }: TagItemProps) => {
const handleSelect = useCallback(() => {
onSelect && onSelect(tag);
}, [onSelect, tag]);
const handleDeselect = useCallback(() => {
onDeselect && onDeselect(tag);
}, [onDeselect, tag]);
return (
<TouchableOpacity
style={[styles.container, isSelected ? styles.selected : null]}
onPress={isSelected ? handleDeselect : handleSelect}
>
<Text style={[styles.text, isSelected ? { color: '#fff' } : null]}>
{capitalizeFirstLetter(tag.name)}
</Text>
</TouchableOpacity>
);
};
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.