我开始学习 React,更具体地说是 ReactJS,现在我有了我的 NodeJS 后端,我在其中存储了一个项目数组,其中包含:id(uuid)、url(string)、techs(字符串数组)、标题(字符串)和喜欢(数字)。
我的组件正在为后端发送到前端的每个项目渲染一个 div,每个 div 将包含项目的所有属性,还有一个按钮,将在我的后端触发路由以增加数字喜欢。它应该在单击按钮的同时更新该数字,我希望这样做而无需再次向 API 请求所有项目列表。
嗯,我的组件是这样的:
import React, { useContext, useEffect, useState } from 'react';
import { ThemeContext } from '../../providers/ThemeContext';
import { Button } from '@material-ui/core';
import axios from '../../services/axios';
function ListProjects() {
const [projects, setProjects] = useState<any[]>([]);
const { theme } = useContext(ThemeContext);
const updateProjects = async () => {
const res = await axios.get('list');
res.data.status === 1 && setProjects(res.data.projects);
};
useEffect(() => {
updateProjects();
}, []);
const onLikeProject = async (id: string) => {
const newObj = (await axios.post(`${id}/like`)).data.updatedProject;
const objIndex = projects.findIndex(i => i.id === id);
let projectsCopy = projects;
/* console.log(projects[objIndex]); */
projectsCopy[objIndex] = newObj;
/* console.log(projectsCopy[objIndex]); */
setProjects(projectsCopy);
/* console.log(projects[objIndex]); */
};
return (
<div style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
minHeight: 'calc(100vh - 64px)',
minWidth: '100vw',
backgroundColor: theme.bg,
color: theme.color
}}>
{ projects && projects.map((p, i) => <div key={p.id} style={{
display: 'flex',
flexDirection: 'column',
width: '550px',
height: '300px',
border: '1px solid blue',
marginBottom: '10px'
}}>
<h3>Title: { p.title }</h3>
<h3>URL: { p.url }</h3>
<h3>Techs: { p.techs.map((t: any, i: any) => i+1 < p.techs.length ? `${t}, ` : t) }</h3>
<h3>Likes: { p.likes }</h3>
<Button color="primary" variant="contained" onClick={() => onLikeProject(p.id) }>Like</Button>
</div>) }
</div>
);
}
export default ListProjects;
如您所见,当页面加载时,项目状态会根据 API 列表响应进行更新。我已经尝试使用相同的 updateProjects()
函数在用户单击“赞”按钮时更新列表,但这会触发另一个 api 调用,这会延迟一点,并对每个“赞”请求发出 api 列表请求, 将打破服务可用请求的可能限制。所以我尝试了 onLikeProject()
中的代码,但它不会更新 UI,即使项目数组正在更新。
我应该如何进行? 谢谢
答案 0 :(得分:2)
状态突变。 \b\w+: +(.(?! +\\w+:))+
是对状态对象 projectsCopy
的引用,它被改变并保存回状态,因此状态数组引用永远不会更新,并且 React 会放弃任何重新渲染。
projects
将状态对象浅拷贝到一个新的数组引用中。
const onLikeProject = async (id: string) => {
const newObj = (await axios.post(`${id}/like`)).data.updatedProject;
const objIndex = projects.findIndex(i => i.id === id);
let projectsCopy = projects;
projectsCopy[objIndex] = newObj; // <-- state mutation
setProjects(projectsCopy);
};
更规范的方法是将前一个状态映射到下一个状态。
const onLikeProject = async (id: string) => {
const newObj = (await axios.post(`${id}/like`)).data.updatedProject;
const objIndex = projects.findIndex(i => i.id === id);
let projectsCopy = [...projects]; // <-- shallow copy state array
projectsCopy[objIndex] = newObj; // <-- then update index
setProjects(projectsCopy);
};