特尔;博士:
改变这个:
this.setState({isEditing: !this.state.isEditing})
this.textInput.current.focus();
对此:
this.setState(previousState => ({isEditing: !previousState.isEditing}), () => {
this.textInput.current.focus();
});
更新:功能组件/钩子
在评论中询问了如何使用useState
功能组件来做到这一点。Rafał Guźniczak 的回答对此进行了解释,但我想提供更多解释和一个可运行的示例。
您仍然不想在设置状态后立即读取状态,但不是使用第二个参数回调到setState
,而是需要在状态更新和组件重新渲染后运行一些代码。我们怎么做?
答案是useEffect
。效果的目的是将外部“事物”(例如:像焦点这样的命令式 DOM 事物)与 React 状态同步:
const { useEffect, useRef, useState } = React;
const { render } = ReactDOM;
function App(props) {
const [isEditing, setIsEditing] = useState(false);
const textInputRef = useRef(null);
const toggleEditing = () => setIsEditing(val => !val);
// whenever isEditing gets set to true, focus the textbox
useEffect(() => {
if (isEditing && textInputRef.current) {
textInputRef.current.focus();
}
}, [isEditing, textInputRef]);
return (
<div>
<button onClick={toggleEditing}>lorem </button>
{isEditing && <input type="text" ref={textInputRef} />}
</div>
);
}
render(
<App />,
document.getElementById('root')
);
<script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script>
<div id="root"></div>
细节:
您遇到了许多人在使用 React 时遇到的一个常见问题,即设置状态是同步的假设。不是。当您调用 setState 时,您是在请求React 更新状态。实际的状态更新发生在稍后。这意味着在setState
调用之后,edit
元素还没有被创建或呈现,所以 ref 指向null
.
从文档:
setState()
将组件状态的更改加入队列,并告诉 React 该组件及其子组件需要使用更新后的状态重新渲染。这是您用来更新用户界面以响应事件处理程序和服务器响应的主要方法。
将其setState()
视为更新组件的请求而不是立即命令。为了更好的感知性能,React 可能会延迟它,然后一次更新多个组件。React 不保证立即应用状态更改。
setState()
并不总是立即更新组件。它可能会批量更新或推迟更新。这使得this.state
在调用setState()
潜在陷阱后立即阅读。相反,使用componentDidUpdate
或setState
回调 ( setState(updater, callback)
),这两者都保证在应用更新后触发。