在条件渲染中使用 refs

IT技术 javascript reactjs reference
2021-05-25 10:20:49

我有参考和条件渲染的问题。我想在单击按钮标签时聚焦输入标签。基本上,我有这个简化的代码。

class App extends React.Component {
  textInput
  constructor(props) {
    super(props)
    this.state = {isEditing: false}
    this.textInput = React.createRef()
  }

    onClick = () => {
        this.setState({isEditing: !this.state.isEditing})
        this.textInput.current.focus();
    }
  render () {
    let edit = this.state.isEditing ?
        (<input type="text" ref={this.textInput} />)
        : ""
    return (
      <div>
            <button onClick={this.onClick}>lorem </button>
            {edit}
      </div>
    );
  }
}

当我单击按钮时,会显示输入标签,但 reftextInput仍设置为null因此我无法聚焦输入。

我找到了一些解决方法,例如:

  • autoFocus在输入标签中设置属性
  • 使用 css 隐藏输入标签时 isEditing == false

但实际上这是一个非常基本的模式,我想知道是否有一个干净的解决方案。

谢谢

2个回答

特尔;博士:

改变这个:

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()潜在陷阱后立即阅读相反,使用componentDidUpdatesetState回调 ( setState(updater, callback)),这两者都保证在应用更新后触发。

非常感谢您的回答@rossipedia。我想知道我是否可以用钩子来做到这一点。

显然你不能像 setState 那样将第二个参数传递给 useState setter。但是你可以像这样使用 useEffect(注意 useEffect 中的第二个参数):

const [isEditing, setIsEditing] = React.useState(false);
React.useEffect(() => {
    if (isEditing) {
        textInput.current.focus();
    }
}, [isEditing]);

const handleClick = () => setIsEditing(isEditing);

它奏效了!;)

来源:https : //www.robinwieruch.de/react-usestate-callback/