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 在我传递给 InfoCard
的 bookmarks
数组道具中,并且 isFavourite
也是如此。 isFavourite
是我在父 Profile
中执行的 includes()
方法的结果。但是 img(名称旁边的粉红色书签轮廓)未填充。您还可以看到我在记录 true
之前记录了三次 false
。该控制台日志位于 InfoCard
的 render()
方法中: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
应该只存在于一处。
我有一个 Profile
组件,它在加载时调用 Django 服务器来获取代表已添加书签的配置文件的 ID 数组。如果书签数组包含用户所在的配置文件的 ID(即,如果用户在 /profile/1
和 [1, 4, 6].includes(1)
),则应填充页面上的 img,否则它应该只是被概述。用户可以单击 img,然后调用后端将该 ID 添加到数组中。再次单击它会将其从数组中删除。
但是,由于一些我不理解的渲染顺序,img 没有正确切换到填充版本。
InfoCard
组件的开发工具信息,它是 Profile
的子组件。您还可以看到配置文件 1 在我传递给 InfoCard
的 bookmarks
数组道具中,并且 isFavourite
也是如此。 isFavourite
是我在父 Profile
中执行的 includes()
方法的结果。但是 img(名称旁边的粉红色书签轮廓)未填充。您还可以看到我在记录 true
之前记录了三次 false
。该控制台日志位于 InfoCard
的 render()
方法中: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
应该只存在于一处。