我们如何知道 React ref.current 值何时发生了变化?

tru*_*ktr 24 javascript reactjs react-ref

正常情况下,有了props,我们就可以写

componentDidUpdate(oldProps) {
  if (oldProps.foo !== this.props.foo) {
    console.log('foo prop changed')
  }
}
Run Code Online (Sandbox Code Playgroud)

为了检测道具的变化。

但是如果我们使用React.createRef(),我们如何检测 ref 何时更改为新组件或 DOM 元素?React 文档并没有真正提及任何内容。

铁,

class Foo extends React.Component {
  someRef = React.createRef()

  componentDidUpdate(oldProps) {
    const refChanged = /* What do we put here? */

    if (refChanged) {
      console.log('new ref value:', this.someRef.current)
    }
  }

  render() {
    // ...
  }
}
Run Code Online (Sandbox Code Playgroud)

我们是否应该自己实现某种旧价值的东西?

铁,

class Foo extends React.Component {
  someRef = React.createRef()
  oldRef = {}

  componentDidMount() {
    this.oldRef.current = this.someRef.current
  }

  componentDidUpdate(oldProps) {
    const refChanged = this.oldRef.current !== this.someRef.current

    if (refChanged) {
      console.log('new ref value:', this.someRef.current)

      this.oldRef.current = this.someRef.current
    }
  }

  render() {
    // ...
  }
}
Run Code Online (Sandbox Code Playgroud)

这是我们应该做的吗?我原以为 React 会为此添加一些简单的功能。

for*_*d04 42

React 文档推荐使用回调引用来检测ref值的变化。

挂钩

export function Comp() {
  const onRefChange = useCallback(node => {
    if (node === null) { 
      // DOM node referenced by ref has been unmounted
    } else {
      // DOM node referenced by ref has changed and exists
    }
  }, []); // adjust deps

  return <h1 ref={onRefChange}>Hey</h1>;
}
Run Code Online (Sandbox Code Playgroud)

useCallback用于防止使用null和 元素重复调用ref 回调。

您可以通过使用以下命令存储当前 DOM 节点来触发重新渲染useState

const [domNode, setDomNode] = useState(null);
const onRefChange = useCallback(node => {
  setDomNode(node); // trigger re-render on changes
  // ...
}, []);
Run Code Online (Sandbox Code Playgroud)

类组件

export class FooClass extends React.Component {
  state = { ref: null, ... };

  onRefChange = node => {
    // same as Hooks example, re-render on changes
    this.setState({ ref: node });
  };

  render() {
    return <h1 ref={this.onRefChange}>Hey</h1>;
  }
}
Run Code Online (Sandbox Code Playgroud)

注意useRef不通知ref更改。还没有运气React.createRef()/对象裁判。

这是一个测试用例,它在触发onRefChange回调时删除并重新添加节点:

export function Comp() {
  const onRefChange = useCallback(node => {
    if (node === null) { 
      // DOM node referenced by ref has been unmounted
    } else {
      // DOM node referenced by ref has changed and exists
    }
  }, []); // adjust deps

  return <h1 ref={onRefChange}>Hey</h1>;
}
Run Code Online (Sandbox Code Playgroud)
const [domNode, setDomNode] = useState(null);
const onRefChange = useCallback(node => {
  setDomNode(node); // trigger re-render on changes
  // ...
}, []);
Run Code Online (Sandbox Code Playgroud)

  • 这是我第一次在对我来说真正有意义的示例中看到 useCallback。谢谢你! (2认同)

Tho*_*lle 3

componentDidUpdate当组件状态或 props 改变时被调用,所以当 a 改变时它不一定会被调用,ref因为它可以根据你认为合适的方式进行改变。

如果您想检查参考是否与之前的渲染相比发生了变化,您可以保留另一个参考来与真实的参考进行检查。

例子

class App extends React.Component {
  prevRef = null;
  ref = React.createRef();
  state = {
    isVisible: true
  };

  componentDidMount() {
    this.prevRef = this.ref.current;

    setTimeout(() => {
      this.setState({ isVisible: false });
    }, 1000);
  }

  componentDidUpdate() {
    if (this.prevRef !== this.ref.current) {
      console.log("ref changed!");
    }

    this.prevRef = this.ref.current;
  }

  render() {
    return this.state.isVisible ? <div ref={this.ref}>Foo</div> : null;
  }
}

ReactDOM.render(<App />, document.getElementById("root"));
Run Code Online (Sandbox Code Playgroud)
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>
Run Code Online (Sandbox Code Playgroud)

  • “ref 是一个可变值,可以被任何东西改变”,这是真的,但类似地,`this.state` 中的任何东西都可以改变,但是我们显然避免这样做,因为这不是改变状态的方法。同样,我认为(希望)显而易见的是我们不应该随意修改 props 或 refs。因此,如果我们只让 React 修改“ref.current”(仅将 ref 传递到 JSX 标记中),那么我们必须跟踪旧值的想法似乎是唯一的方法。如果 React 在这方面有更多的准备就太好了。 (2认同)