编辑:
发生错误是因为我将数组作为第二个参数传递给useEffect
。即使数组中的值保持不变,引用也不断变化,因此useEffect
被不断调用并重置我的复选框值。该数组是通过useState
调用创建的。我将useState
替换为useReducer
(如果对象实际更改,则归约器仅更改对象引用),并在组件树的较高位置更新了一些缺少的依赖项。
原始问题:
我无法更新功能组件中的状态。
我的问题与此类似: React SetState doesn't call render
我已经在复制状态对象(通过使用array.filter)而不是引用它;但是我的状态仍然没有更新。
为了跟踪问题,我尝试在一个最小的示例中重新创建问题:
但是在我的最小示例中,一切正常。我无法重现该错误。
这是我的状态不更新的示例:
configCheckboxGroup.tsx:
import classNames from "classnames";
import React, { useState, useEffect } from "react";
import { Component } from "../../model";
import CheckboxPanel from "./panels/checkboxPanel";
interface configCheckboxGroupProps {
className?: string;
choices: Array<Component>;
selected: Array<string>;
addToCart: (items: Array<Component>) => void;
}
const ConfigCheckboxGroup: React.SFC<configCheckboxGroupProps> = ({
className,
choices,
selected,
addToCart,
}) => {
const [ selectedComp, setSelectedComp ] = useState<Array<string>>(selected);
// device loads later, selected has to be updated
useEffect(() => {
setSelectedComp(selected);
}, [selected]);
const handleOnChange = (ev: React.FormEvent, id: string) => {
console.debug(id);
console.debug(selectedComp.filter(el => el !== id));
if (selectedComp.includes(id)) {
// was already checked || this line is not working!
setSelectedComp(selectedComp.filter(el => el !== id));
} else {
// was not checked
setSelectedComp([...(selectedComp), id]);
}
const selected = choices.filter(el => selectedComp.includes(el.reference._id));
addToCart(selected);
};
return (
<div className={classNames("panellist", className)}>
{
choices.map(el => {
return (
<CheckboxPanel
image={ el.reference.picture ? el.reference.picture : undefined }
name={ el.reference.name }
id={ el.reference._id }
price={ el.reference.price ? el.reference.price :
el.price ? el.price : 0 }
key={ el._id }
checked={ selectedComp.includes(el.reference._id) }
onChange={ handleOnChange }
/>
)
})
}
<span>
{ selectedComp }
</span>
</div>
)
}
export default ConfigCheckboxGroup;
和checkboxPanel.tsx:
import classNames from "classnames";
import React from "react";
import "./checkboxPanel.scss";
import noImage from "../../../resources/images/errors/no-image.svg";
interface PanelProps {
className?: string;
image?: string;
name: string;
id: string;
price: number;
checked: boolean;
onChange: (ev: React.FormEvent, id: string) => void;
}
const CheckboxPanel: React.SFC<PanelProps> = ({
className,
image,
name,
id,
price,
checked,
onChange,
}) => {
const getImage = () => {
if (image) {
return image;
} else {
return noImage;
}
}
return (
<div className={classNames("panel", "checkbox-panel", className)}>
<div className="top">
<div className="image">
<img alt="Product" src={getImage()} />
</div>
<div className="name">
{name}
</div>
</div>
<div className="bottom">
<div className="category">
{ Number(price).toFixed(2) } €
</div>
<div>
<input type="checkbox"
checked={ checked }
onChange={ (e) => onChange(e, id) }
/>
</div>
</div>
</div>
)
};
export default CheckboxPanel;
示例之间的唯一区别是在第二个示例中,我在子组件内部调用了handle函数。但是我在其他场合也做同样的事情:我有一个非常相似的Component configRadioGroup,它带有单选按钮,而不是复选框,在该复选框中一切正常。
我尝试通过手动过滤数组并尝试许多其他方法来玩耍,但似乎无济于事。这就是为什么我在最后一次尝试时要问这个问题(尽管我知道由于这个问题非常具体,所以这个问题不是一个好问题)。
答案 0 :(得分:4)
如果您将控制台日志放入useEffect中,那么更改道具selected
将重置selectedComp
,您可能会发现每次重置该道具。
您需要跟踪所选内容的来源(redux?)及其设置方式(addToCart?)。
一个肮脏的解决方法是仅在安装组件时设置selectedComp
,这很肮脏,并且会/应该导致react-hooks / exhaustive-deps棉绒触发:
useEffect(() => {
setSelectedComp(selected);
}, []);
但是最好跟踪selected
出了什么问题,如果它来自redux,则可以直接使用selected
并省去selectedComp
,因为这只是一个副本。