React useState setter 不会更新上下文中的值
React useState setter wont update the value inside a context
所以,我正在使用 React 上下文 API 来管理我的应用程序的一些全局状态
const blueprintList = [
{
id: new Date().toISOString(),
name: 'Getting started',
color: '#c04af6',
code: '<h1>Hello World!</h1>,
},
];
export const BlueprintsContext = createContext();
export const BlueprintsProvider = ({ children }) => {
const [blueprints, setBlueprints] = useState(blueprintList);
let [selected, setSelected] = useState(0);
const [count, setCount] = useState(blueprints.length);
useEffect(() => {
setCount(blueprints.length);
}, [blueprints]);
// just for debugging
useEffect(() => {
console.log(`DEBUG ==> ${selected}`);
}, [selected]);
const create = blueprint => {
blueprint.id = new Date().toISOString();
setBlueprints(blueprints => [...blueprints, blueprint]);
setSelected(count);
};
const remove = blueprint => {
if (count === 1) return;
setBlueprints(blueprints => blueprints.filter(b => b.id !== blueprint.id));
setSelected(-1);
};
const select = blueprint => {
const index = blueprints.indexOf(blueprint);
if (index !== -1) {
setSelected(index);
}
};
const value = {
blueprints,
count,
selected,
select,
create,
remove,
};
return (
<BlueprintsContext.Provider value={value}>
{children}
</BlueprintsContext.Provider>
);
};
然后我像这样在组件内部使用 remove 函数。
const Blueprint = ({ blueprint, isSelected }) => {
const { select, remove } = useContext(BlueprintsContext);
return (
<div
className={classnames(`${CSS.blueprint}`, {
[`${CSS.blueprintActive}`]: isSelected,
})}
key={blueprint.id}
onClick={() => select(blueprint)}
>
<div
className={CSS.blueprintDot}
style={{ backgroundColor: blueprint.color }}
/>
<span
className={classnames(`${CSS.blueprintText}`, {
['text-gray-300']: isSelected,
['text-gray-600']: !isSelected,
})}
>
{blueprint.name}
</span>
{isSelected && (
<IoMdClose className={CSS.close} onClick={() => remove(blueprint)} />
)}
</div>
);
};
父组件接收项目列表并为每个项目呈现一个 Blueprint 组件。问题是当按下 IoMdClose 图标时,该项目将从列表中删除,但 selected 值不会更新为 -1。 ♂️
再见,我发现了你的问题:在函数 select
中你已经设置了 selected
:
const select = blueprint => {
const index = blueprints.indexOf(blueprint);
if (index !== -1) {
setSelected(index); // here you set selected
}
};
当您点击 IoMdClose
图标时,也会触发 select
。所以结果是这个 useEffect
:
useEffect(() => {
console.log(`DEBUG ==> ${selected}`);
}, [selected]);
不记录任何内容。
我试图从 select
函数中删除 setSelected(index);
,当我点击 IoMdClose
图标时,selected
将被设置为 -1 并且 useEffect 日志 DEBUG ==> -1
.
但现在您遇到了另一个问题:如果您从 select
中删除 setSelected(index);
并尝试从左侧树视图中 select 一个蓝图,蓝图将不会 select编辑。所以我在select
函数中re-addedsetSelected(index);
。从 remove
函数中删除了 setSelected(-1);
,现在 useEffect 不记录任何内容!
我认为发生这种情况是因为您试图将 selected
设置为不存在的索引(因为您在单击图标时删除了蓝图)。为了验证这一点,我将 select
函数中的 setSelected(index);
修改为 setSelected(0);
并且现在如果我删除一个蓝图,useEffect
将被触发并记录 DEBUG ==> 0
.
如果 setSelected(-1);
背后的想法是取消 select 树视图中的所有蓝图,您可以这样做:
export const BlueprintsProvider = ({ children }) => {
....
const removeSelection = useRef(0);
useEffect(() => {
if (blueprints.length < removeSelection.current) setSelected(-1); // if I removed a blueprint, then fire setSelected(-1);
setCount(blueprints.length);
removeSelection.current = blueprints.length; //removeSelection.current setted as blueprints.length
}, [blueprints]);
当然还有从 remove
函数中删除 setSelected(-1);
。
所以,我正在使用 React 上下文 API 来管理我的应用程序的一些全局状态
const blueprintList = [
{
id: new Date().toISOString(),
name: 'Getting started',
color: '#c04af6',
code: '<h1>Hello World!</h1>,
},
];
export const BlueprintsContext = createContext();
export const BlueprintsProvider = ({ children }) => {
const [blueprints, setBlueprints] = useState(blueprintList);
let [selected, setSelected] = useState(0);
const [count, setCount] = useState(blueprints.length);
useEffect(() => {
setCount(blueprints.length);
}, [blueprints]);
// just for debugging
useEffect(() => {
console.log(`DEBUG ==> ${selected}`);
}, [selected]);
const create = blueprint => {
blueprint.id = new Date().toISOString();
setBlueprints(blueprints => [...blueprints, blueprint]);
setSelected(count);
};
const remove = blueprint => {
if (count === 1) return;
setBlueprints(blueprints => blueprints.filter(b => b.id !== blueprint.id));
setSelected(-1);
};
const select = blueprint => {
const index = blueprints.indexOf(blueprint);
if (index !== -1) {
setSelected(index);
}
};
const value = {
blueprints,
count,
selected,
select,
create,
remove,
};
return (
<BlueprintsContext.Provider value={value}>
{children}
</BlueprintsContext.Provider>
);
};
然后我像这样在组件内部使用 remove 函数。
const Blueprint = ({ blueprint, isSelected }) => {
const { select, remove } = useContext(BlueprintsContext);
return (
<div
className={classnames(`${CSS.blueprint}`, {
[`${CSS.blueprintActive}`]: isSelected,
})}
key={blueprint.id}
onClick={() => select(blueprint)}
>
<div
className={CSS.blueprintDot}
style={{ backgroundColor: blueprint.color }}
/>
<span
className={classnames(`${CSS.blueprintText}`, {
['text-gray-300']: isSelected,
['text-gray-600']: !isSelected,
})}
>
{blueprint.name}
</span>
{isSelected && (
<IoMdClose className={CSS.close} onClick={() => remove(blueprint)} />
)}
</div>
);
};
父组件接收项目列表并为每个项目呈现一个 Blueprint 组件。问题是当按下 IoMdClose 图标时,该项目将从列表中删除,但 selected 值不会更新为 -1。 ♂️
再见,我发现了你的问题:在函数 select
中你已经设置了 selected
:
const select = blueprint => {
const index = blueprints.indexOf(blueprint);
if (index !== -1) {
setSelected(index); // here you set selected
}
};
当您点击 IoMdClose
图标时,也会触发 select
。所以结果是这个 useEffect
:
useEffect(() => {
console.log(`DEBUG ==> ${selected}`);
}, [selected]);
不记录任何内容。
我试图从 select
函数中删除 setSelected(index);
,当我点击 IoMdClose
图标时,selected
将被设置为 -1 并且 useEffect 日志 DEBUG ==> -1
.
但现在您遇到了另一个问题:如果您从 select
中删除 setSelected(index);
并尝试从左侧树视图中 select 一个蓝图,蓝图将不会 select编辑。所以我在select
函数中re-addedsetSelected(index);
。从 remove
函数中删除了 setSelected(-1);
,现在 useEffect 不记录任何内容!
我认为发生这种情况是因为您试图将 selected
设置为不存在的索引(因为您在单击图标时删除了蓝图)。为了验证这一点,我将 select
函数中的 setSelected(index);
修改为 setSelected(0);
并且现在如果我删除一个蓝图,useEffect
将被触发并记录 DEBUG ==> 0
.
如果 setSelected(-1);
背后的想法是取消 select 树视图中的所有蓝图,您可以这样做:
export const BlueprintsProvider = ({ children }) => {
....
const removeSelection = useRef(0);
useEffect(() => {
if (blueprints.length < removeSelection.current) setSelected(-1); // if I removed a blueprint, then fire setSelected(-1);
setCount(blueprints.length);
removeSelection.current = blueprints.length; //removeSelection.current setted as blueprints.length
}, [blueprints]);
当然还有从 remove
函数中删除 setSelected(-1);
。