useImperativeHandle 用于子组件但无法从父组件获取函数

useImperativeHandle usage for children componenent but cannot get function from parent

我有反应写的应用程序:

主要应用程序是:

function App({articles}) {
    ...
    const SortVote=()=>{
      console.log(childRef.current);
      console.log(Object.getOwnPropertyNames(childRef.current))
      childRef.current.SortVote();
    }
  
    const [art, setArt]= useState(articles);
    const childRef = useRef();

    return (
        <div className="App">
            <h8k-navbar header={title}></h8k-navbar>
            <div className="layout-row align-items-center justify-content-center my-20 navigation">
                <label className="form-hint mb-0 text-uppercase font-weight-light">Sort By</label>
                <button data-testid="most-upvoted-link" className="small" onClick={SortVote}>Most Upvoted</button>
            </div>
            <Articles articles={art} cRef={childRef}/>
        </div>
    );

}

子组件是:

function Articles({ articles, cRef }) {
  const [arts, setArts] = useState(articles);
  const sortVote = () => {
    const result = articles.sort((a, b) => {
      if (a.upvotes > b.upvotes) {
        return -1;
      }
      if (a.upvotes < b.upvotes) {
        return 1;
      }
      return 0;
    });
    setArts(result);
  };
  const sortDate = () => {
    const result = articles.sort((a, b) => {
      if (a.date > b.date) {
        return -1;
      }
      if (a.date < b.date) {
        return 1;
      }
      return 0;
    });
    setArts(result);
  };
  useImperativeHandle(cRef, () => ({
    sortVote,
    sortDate
  }));
  if (articles)
    return (
      <div className="card w-50 mx-auto">
        <table>
          <thead>
            <tr>
              <th>Title</th>
              <th>Upvotes</th>
              <th>Date</th>
            </tr>
          </thead>
          <tbody>
            {arts.map((item, index) => (
              <tr data-testid="article" key={index}>
                <td data-testid="article-title">{item.title}</td>
                <td data-testid="article-upvotes">{item.upvotes}</td>
                <td data-testid="article-date">{item.date}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    );
}

我在子组件中使用 useImperativeHandle 并尝试在父组件中使用 'childRef.current.SortVote()' 来调用子组件函数。但是浏览器报错:

childRef.current.SortVote is not a function

完整代码开启:https://stackblitz.com/edit/react-vy7df9

在useImpertiveHandle hook时,SortVote和sortDate是已经定义好的函数,为什么这里还是报错SortVote is not function?

问题

useImperativeHandle 与传递的 React 引用一起使用,不是持有引用值的道具

useImperativeHandle

useImperativeHandle customizes the instance value that is exposed to parent components when using ref. As always, imperative code using refs should be avoided in most cases. useImperativeHandle should be used with forwardRef:

function FancyInput(props, ref) {
  const inputRef = useRef();
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    }
  }));
  return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);

在您的代码中,您没有将 ref 传递给子组件,而是传递了一个具有 ref 值的 prop。

<Articles cRef={childRef} articles={art} />

<Articles ref={childRef} articles={art} />

解决方案

首先,更新 Articles 组件的函数签名以使用 React 引用。请注意,这是传递给函数的 second 参数,props 对象是第一个。

function Articles({ articles }, ref) {
  ...

  useImperativeHandle(ref, () => ({
    sortVote,
    sortDate
  }));

  if (articles) {
    return (
      ...
    );
  }
  return null;
}

接下来,将 Articles 组件包装在 React.forwardRef 中。

Articles = forwardRef(Articles);

最后,将 ref 传递给 Articles 组件。注意,是sortVote,不是SortVote.

function App({articles}) {
  const [art, setArt]= useState(articles);
  const childRef = useRef();

  ...
  const SortVote=()=>{
    console.log(childRef.current);
    console.log(Object.getOwnPropertyNames(childRef.current))
    childRef.current.sortVote();
  }

  return (
    <div className="App">
      ...
      <Articles
        ref={childRef}
        articles={art}
      />
    </div>
  );
}

更新

您的子 Articles 组件中也存在错误。 Array.prototype.sort 进行就地排序,这意味着它会改变 arts 状态。您首先需要在排序之前复制数组。 Array.prototype.slice 是一种简单的函数式编程方法,可在排序前内联复制数组。

function Articles({ articles }, ref) {
  const [arts, setArts] = useState(articles);

  const sortVote = () => {
    const result = articles.slice().sort((a, b) => {
      if (a.upvotes > b.upvotes) {
        return -1;
      }
      if (a.upvotes < b.upvotes) {
        return 1;
      }
      return 0;
    });
    setArts(result);
  };

  const sortDate = () => {
    const result = articles.slice().sort((a, b) => {
      if (a.date > b.date) {
        return -1;
      }
      if (a.date < b.date) {
        return 1;
      }
      return 0;
    });
    setArts(result);
  };

  ...