2

我有通过反应编写的应用程序:

主要应用程序是:

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 钩子中,SortVote 和 sortDate 已经定义了函数,但是为什么这里仍然报错 SortVote is not function?

4

1 回答 1

1

问题

useImperativeHandle与传递的React refs 一起使用,而不是与保存 ref 值的 props一起使用。

使用命令式句柄

useImperativeHandle自定义使用时暴露给父组件的实例值ref。与往常一样,在大多数情况下应避免使用 refs 的命令式代码。useImperativeHandle应与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 ref。请注意,这是传递给函数的第二个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);
  };

  ...
于 2021-08-28T04:48:14.423 回答