我已经试着问了几十次这个难以回答的问题。我已经做了一个最简单的例子来问这个问题。
我在handleChange
方法中更改了钩子的值。但console.log
总是显示以前的值,而不是新的值。为什么?
我需要更改钩子的值,然后用它来做其他事情,而不是做console.log
。但我做不到,因为钩子总是没有我刚想放进去的东西。
const options = ["Option 1", "Option 2"];
export default function ControllableStates() {
const [value, setValue] = React.useState(options[0]);
const handleChange = val => {
setValue(val);
console.log(value);
};
return (
<div>
<div>{value}</div>
<br />
<Autocomplete
value={value}
onChange={(event, newValue) => {
handleChange(newValue);
}}
options={options}
renderInput={params => (
<TextField {...params} label="Controllable" variant="outlined" />
)}
/>
</div>
);
}
你可以在这里试试。https://codesandbox.io/s/awesome-lumiere-y2dww?file=/src/App.js
我认为问题在于您将值记录在handleChange
函数中。控制台记录函数之外的值会记录正确的值。链接:https://codesandbox.io/s/async-fast-6y71b
钩子不会立即更新您想要更新的值,正如您对类所期望的那样(尽管这也不能保证)
状态挂钩,当调用setValue
时将触发重新渲染。在该新渲染中,状态将具有预期的新值。这就是为什么console.log
看到旧值的原因。
可以想象,在每次渲染中,状态值只是该组件函数调用的局部变量。并将渲染的结果视为该渲染调用中状态+道具的结果。无论这两个选项中的任何一个发生变化(父组件中的道具;setXXX函数中的状态),都会触发新的渲染。
如果将console.log
移出回调处理程序的外部(即,在渲染的主体中),您将在交互后发生的渲染中看到状态被正确记录。
从这个意义上说,在交互的回调事件中,你只需要担心正确更新状态,下一次渲染会注意,在新的道具/状态下,重新渲染结果
该值不会同步"更改"-它甚至是用const
声明的,因此即使它在同一范围内更改的概念也没有意义。
当使用钩子更改状态时,当重新绘制组件时,会看到新值。因此,要使用"新值"进行日志记录和操作,请在函数的主体中进行检查:
const ControllableStates = () => {
const [value, setValue] = React.useState(options[0]);
const handleChange = val => {
setValue(val);
};
// ADD LOG HERE:
console.log('New or updated value:', value);
return (
<div>
<div>{value}</div>
<br />
<Autocomplete
value={value}
onChange={(event, newValue) => {
handleChange(newValue);
}}
options={options}
renderInput={params => (
<TextField {...params} label="Controllable" variant="outlined" />
)}
/>
</div>
);
}
您打印的是handleChange
中的旧value
,而不是新val
。即
const handleChange = val => {
setValue(val);
console.log(value);
};
应为:
const handleChange = val => {
setValue(val);
console.log(val);
};
实际上,让我们回顾一下这个场景背后的逻辑。您应该只使用"handleChange"函数来更新状态挂钩,并让其他事情来执行。逻辑取决于该状态挂钩值,这主要是使用"useEffect"挂钩完成的。
你可以重构你的代码如下:
const handleChange = val => {
setValue(val);
};
React.useEffect(() => {
console.log(value);
// do your logic here
}, [value])
所以我认为主要的问题是你不了解React处理组件和状态。
因此,我将大大简化React的工作。
- React渲染一个新组件并记住它的状态和输入(aka道具),这是孩子们的状态和投入
- 如果在任何给定点输入或状态发生变化,React将呈现通过调用组件函数再次调用组件
考虑一下:
function SomeComponent(text) {
return (<div>The <i>text</i> prop has the value {text}</div>)
}
假设初始prop值为"abc"
,React将调用SomeComponent("abc")
,然后函数返回CCD_ 14和React将对此进行渲染。如果道具text
没有改变,那么React就什么都不做了。
现在父组件将prop更改为"def"
,现在React将调用SomeComponent("def")
,它将返回<div>The <i>text</i> prop has the value def</div>
,这与最后一次调用,因此React将更新DOM以反映更改。
现在让我们介绍一下状态
function SomeComponent() {
const [name, setName] = React.useState("John")
function doSomething()
{
alert("The name is " + name)
}
return (
<p>Current name: {name}</p>
<button onClick={() => setName("Mary")}>Set name to Mary</button>
<button onClick={() => setName("James")}>Set name to James</button>
<button onClick={() => doSomething()}>Show current name</button>
)
}
因此这里React将调用SomeComponent()
并呈现名称John和3按钮请注意,name
变量的值在当前执行,因为它被声明为const
。此变量仅反映状态的最新值。
当您按下第一个按钮时,将执行setName()
。React将在内部存储状态的新值,并且由于状态的更改,它将呈现则SomeComponent()
将被再次调用。现在变量name
将再次反映状态的最新值(useState
就是这样做的),因此在本例中Mary。反应将意识到DOM必须更新,并打印名称Mary。
如果按下第三个按钮,它将调用doSomething()
,CCD_26将打印name
变量的最新值,因为每次React调用SomeComponent()
,使用最新的CCD_ 30的值。所以,一旦你打电话给setName()
,你就不需要任何特别的东西都可以获得新的价值。React将负责调用组件功能。
因此,当你不使用类组件而是使用函数组件时,你必须思考不同的是:React一直在调用函数执行它反映特定时间点的最新状态。所以当你调用useState
钩子的setter,您知道组件函数将并且CCD_ 33将返回新的值。
我建议您阅读这篇文章,也请再次阅读组件和React文档中的道具。
那么你应该如何进行呢?像这样:
const options = ["Option 1", "Option 2"];
export default function ControllableStates() {
const [value, setValue] = React.useState(options[0]);
const handleChange = val => {
setValue(val);
console.log(value);
};
const handleClick = () => {
// DOING SOMETHING WITH value
alter(`Now I'm going to do send ${value}`);
}
return (
<div>
<div>{value}</div>
<br />
<Autocomplete
value={value}
onChange={(event, newValue) => {
handleChange(newValue);
}}
options={options}
renderInput={params => (
<TextField {...params} label="Controllable" variant="outlined" />
)}
/>
<button type="button" onClick={handleClick}>Send selected option</button>
</div>
);
}
请参阅CodeSandbox。