React useImperativeHandle和forwardRef被设置,引用似乎没有更新

时间:2020-03-09 11:43:59

标签: javascript reactjs react-hooks react-forwardref

我需要访问子组件的位置。据我了解,要访问子属性,我需要使用useImperativeHandle将子API添加到其引用中。此外,我需要使用forwardRef将引用从父级传输到子级。所以我这样做了:

const Text = React.forwardRef(({ onClick }, ref) => {
  const componentAPI = {};
  componentAPI.getLocation = () => {
    return ref.current.getBoundingClientRect ? ref.current.getBoundingClientRect() : 'nope'
  };
  React.useImperativeHandle(ref, () => componentAPI);
  return (<button onClick={onClick} ref={ref}>Press Me</button>);
});

Text.displayName = "Text";

const App = () => {
  const ref = React.createRef();
  const [value, setValue] = React.useState(null)

  return (<div>
    <Text onClick={() => setValue(ref.current.getLocation())} ref={ref} />
    <div>Value: {JSON.stringify(value)}</div>
    </div>);
};

ReactDOM.render(<App />, document.querySelector("#app"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="app"></div>

如您所见,ref没有getBoundingClientRect属性,但是如果我这样做,它将按预期工作:

const App = () => {
  const ref = React.createRef();
  const [value, setValue] = React.useState(null)

  return (<div>
      <button ref={ref} onClick={() => setValue(ref.current.getBoundingClientRect()) } ref={ref}>Press Me</button>
      <div>Value: {JSON.stringify(value)}</div>
    </div>);
};

ReactDOM.render(<App />, document.querySelector("#app"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="app"></div>

那么我对useImperativeHanedleforwardRef的理解又有什么问题呢?

3 个答案:

答案 0 :(得分:1)

docs所示(可能不够理解),子组件本身应具有不同的ref,并且通过useImperativeHandle可以定义一个将forwardedRef映射到子ref的函数:

import React from 'react'
import ReactDOM from 'react-dom'
const Text = React.forwardRef(({ onClick }, ref) => {
  const buttonRef = React.useRef() // create a new ref for button
  const componentAPI = {};
  componentAPI.getLocation = () => {
    return buttonRef.current.getBoundingClientRect ? buttonRef.current.getBoundingClientRect() : 'nope' // use buttonRef here
  };
  React.useImperativeHandle(ref, () => componentAPI); // this maps ref to buttonRef now
  return (<button onClick={onClick} ref={buttonRef}>Press Me</button>); // set buttonRef
});

Text.displayName = "Text";

const App = () => {
  const ref = React.useRef();
  const [value, setValue] = React.useState(null)

  return (<div>
    <Text onClick={() => setValue(ref.current.getLocation())} ref={ref} />
    <div>Value: {JSON.stringify(value)}</div>
    </div>);
};

ReactDOM.render(<App />, document.querySelector("#app"))

答案 1 :(得分:1)

要使用func1() { echo $BASHPID; } echo $BASHPID 28365 ( func1 ) 28627 ,您需要使用另一个useImperativeHandle实例,如下所示:

ref

如果您希望您的逻辑有效(使用转发的const Text = React.forwardRef(({ onClick }, ref) => { const buttonRef = React.useRef(); React.useImperativeHandle( ref, () => ({ getLocation: () => buttonRef.current.getBoundingClientRect() }), [buttonRef] ); return ( <button onClick={onClick} ref={buttonRef}> Press Me </button> ); }); ),这将起作用:

ref

为什么您的示例不起作用?

因为const Text = React.forwardRef(({ onClick }, ref) => { React.useEffect(() => { ref.current.getLocation = ref.current.getBoundingClientRect; }, [ref]); return ( <button onClick={onClick} ref={ref}> Press Me </button> ); }); ref.current.getBoundingClientRect中分配(尝试记录它)时不可用,因为您实际上用useImperativeHandle覆盖了按钮的ref(请选中{{1 }},在挂载后为useImperativeHandle分配了Text3

Edit admiring-darkness-o8bm4

答案 2 :(得分:0)

我只是想添加此答案,以显示在删除无用的过度控制时事情如何变得更容易...

const Text = React.forwardRef(({ onClick }, ref) => {
  ref.getLocation = () => ref.current && ref.current.getBoundingClientRect()
  return (<button onClick={onClick} ref={ref}>Press Me</button>);
});

Text.displayName = "Text";

function App() {
  const ref = { current: null };
  const [value, setValue] = React.useState(null)
  return (<div>
    <Text onClick={() => setValue(ref.getLocation())} ref={ref} />
    <div>Value: {JSON.stringify(value)}</div>
  </div>);
}


ReactDOM.render(<App />, document.querySelector("#app"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="app"></div>

在上面的代码中,我们只使用forwardRef并将子API附加到它的ref上,这看起来很自然,而且非常人性化。

唯一会阻止您使用此方法的是React.createRef调用了Object.preventExtension()(这使我的生活更加艰难...),因此,改用{ current: null }Object.preventExtension()(基本相同)。