Array.includes 在 React 组件加载时返回冲突的结果

Array.includes returning conflicting results on react component load

我有一个 Profile 组件,它在加载时调用 Django 服务器来获取代表已添加书签的配置文件的 ID 数组。如果书签数组包含用户所在的配置文件的 ID(即,如果用户在 /profile/1[1, 4, 6].includes(1)),则应填充页面上的 img,否则它应该只是被概述。用户可以单击 img,然后调用后端将该 ID 添加到数组中。再次单击它会将其从数组中删除。

但是,由于一些我不理解的渲染顺序,img 没有正确切换到填充版本。 在这里您可以看到 InfoCard 组件的开发工具信息,它是 Profile 的子组件。您还可以看到配置文件 1 在我传递给 InfoCardbookmarks 数组道具中,并且 isFavourite 也是如此。 isFavourite 是我在父 Profile 中执行的 includes() 方法的结果。但是 img(名称旁边的粉红色书签轮廓)未填充。您还可以看到我在记录 true 之前记录了三次 false。该控制台日志位于 InfoCardrender() 方法中:console.log(this.props.isFavourite);.

以下是我对所有这些进行编程的方式:

Profile.js

  const [bookmarks, setBookmarks] = useState([]);
  const [isFavourite, setIsFavourite] = useState(false);

  const isUser = checkUser(
    localStorage.getItem("CONNECTORY_userId"),
    match.params.userId
  );

  const changeId = parseInt(localStorage.getItem("CONNECTORY_changeId"));

  useEffect(() => {
    async function fetchBookmarks() {
      const bookmarksData = await fetch(
        `${config.url}/profile/favouriteProfiles/${changeId}`
      ).then((res) => res.json());
      const reducedBookmarksData = bookmarksData.map(({ id }) => id);
      setBookmarks(reducedBookmarksData);
      const isFavouriteCheck =
        reducedBookmarksData.length > 0
          ? reducedBookmarksData.includes(parseInt(match.params.userId, 10))
          : false;
      setIsFavourite(isFavouriteCheck);
    }
    fetchBookmarks();
  }, [match.params.userId, history, changeId, isFavourite]);

  const handleChildBookmarkClick = async (id) => {
    await fetch(`${config.url}/user/favourites/${changeId}/${id}`);
  };

  return (
    <>
        {profileData && (
          <InfoCard
            data={profileData}
            experienceData={
              profileData.experience ? profileData.experience[0] : null
            }
            isUser={isUser}
            isFavourite={isFavourite}
            bookmarks={bookmarks}
            handleBookmarkClick={handleChildBookmarkClick}
          />
        )}
    </>
  );

InfoCard.js

export default class InfoCard extends Component {
  constructor(props) {
    super(props);
    this.state = {
      favourite: this.props.isFavourite,
    };
  }

  componentDidMount = () => {
    this.setState({ favourite: this.props.isFavourite });
  };

  handleBookmarkClick = () => {
    if (this.props.isUser) {
      return;
    }
    this.props.handleBookmarkClick(this.props.data.id);
    this.setState((prevState) => ({ favourite: !prevState.favourite }));
  };

  render() {
    console.log(this.props.isFavourite);
  ...
  {!this.props.isUser && (
     <button
       className="bookmark-container"
       onClick={this.handleBookmarkClick}
     >
       <img
         className="info-card-bookmark"
         src={this.state.favourite ? bookmarkFull : bookmarkEmpty}
         alt="Add this profile to your bookmarks"
       />
     </button>
   )}
  ...

我渲染的顺序是否正确?我觉得也可能和异步函数有关,我在这方面也比较弱

下面的代码:

const handleChildBookmarkClick = async (id) => {
 await fetch(`${config.url}/user/favourites/${changeId}/${id}`);
};

请求成功时未执行任何操作。如果我理解正确,它应该调用 setIsFavourite 或以某种方式触发 fetchBookmarks 以便 Profile 重新获取数据并更新状态(这似乎有点矫枉过正)。

下面的代码看起来也有问题:

componentDidMount = () => {
 this.setState({ favourite: this.props.isFavourite });
};

你应该直接使用像this.props.isFavourite这样的道具或者使用getDerivedStateFromProps。将道具克隆到状态通常不是一个好主意,因为它会断开父子关系并复制状态。
例如,在这种情况下,您最终没有更新配置文件的 isFavourite 状态。 isFavourite 应该只存在于一处。