reactjs - 在 onClick 函数中调用更新函数时 useState 不重新渲染
问题描述
我花了几天时间查看类似的帖子,但没有解决方案。由于某种原因,当我使用setPostState(myState.posts);
它时不会重新渲染组件。
我正在使用反应 ^16.10.2
下面是我的代码:
import React, {useState, useCallback} from 'react';
import {withStyles, makeStyles} from '@material-ui/core/styles';
import {Paper, TableRow, TableHead, TableCell, TableBody, Table, Badge, Fab} from '@material-ui/core'
import {myState} from '../../PubSub/pub-sub'
import ThumbUpIcon from '@material-ui/icons/ThumbUp';
import ThumbDownIcon from '@material-ui/icons/ThumbDown';
const StyledTableCell = withStyles(...))(TableCell);
const StyledTableRow = withStyles(...))(TableRow);
const useStyles = makeStyles(theme => (...));
export default props => {
console.log("++++++++++++++++Render Body+++++++++++++++++++++");
const classes = useStyles();
let [postState, setPostState] = useState(myState.posts);// why does setPostState not update badge count???? or re-render component???
let upVote = (id) => {
let objIndex = myState.posts.findIndex((obj => obj.id == id));
return (
<Fab key={"upVote4309lk" +id} color="primary" aria-label="add" className={classes.fab}
onClick={() => {
myState.posts[objIndex].up_vote++;
setPostState(myState.posts);//why does this not update badge count???? or re-render component???
}}>
<Badge key={"Ubadge"+objIndex} className={classes.margin} badgeContent={postState[objIndex].up_vote} color="primary"><
ThumbUpIcon> </ThumbUpIcon>
</Badge>
</Fab>
)
};
let downVote = (id) => {
let objIndex = myState.posts.findIndex((obj => obj.id == id));
return (
<Fab key={"downVote0940v" + id} color="primary" aria-label="add" className={classes.fab}
onClick={() => {
myState.posts[objIndex].down_vote++;
setPostState(myState.posts);//why does this not update badge count???? or re-render component???
}}>
<Badge className={classes.margin} badgeContent={myState.posts[objIndex].down_vote} color="primary"><
ThumbDownIcon> </ThumbDownIcon>
</Badge>
</Fab>
)
};
function filter(name) {
return name.toLowerCase().includes(props.searchData.title.toLowerCase());
}
function createData(title, description, user, up_votes, down_votes, id) {
if (filter(title, description, user, up_votes, down_votes)) {
return (
<StyledTableRow key={id + "tableKey"}>
<StyledTableCell>{title}</StyledTableCell>
< StyledTableCell>{description}</StyledTableCell>
<StyledTableCell>{user}</StyledTableCell>
<StyledTableCell>{upVote(id)}</StyledTableCell>
<StyledTableCell>{downVote(id)}</StyledTableCell>
</StyledTableRow>
)
}
}
const rows = myState.posts.map(
obj => createData(obj.title, obj.description, obj.user, obj.up_votes, obj.down_votes, obj.id)
);
return (
<Paper className={classes.root}>
<Table className={classes.table} aria-label="customized table">
<TableHead>
<TableRow>
<StyledTableCell>Title</StyledTableCell>
<StyledTableCell>Description</StyledTableCell>
<StyledTableCell>User</StyledTableCell>
<StyledTableCell>Up Votes</StyledTableCell>
<StyledTableCell>Down Votes</StyledTableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map(row => (row))}
</TableBody>
</Table>
</Paper>
);
}
任何帮助都会很棒,谢谢!
解决方案
在 React 中,组件仅在更改时重新渲染,state
即prevState
!==currentState
它是基于类的组件还是功能组件。在您的情况下,您正在调用 setPosts 但它不会更改状态,因为您myState.posts
在设置状态时分配了相同的对象。React 不对对象执行深度相等检查,它只是比较对象在某个状态下的引用。在您的情况下,引用永远不会随着您的变化而改变,并且在调用 setPosts 后prevState
保持等于。newState
为了避免这个问题,在 react 中使用 Objects / Arrays 设置状态时,您需要确保分配新的引用。所以比较prevState
并currState
返回false。有关更多详细信息,请参阅相等检查示例
访问和设置状态的正确方法:
// Set the initial value using myState.posts and then use the variable
// postState to access the posts and not myState.posts
const [postState, setPostState] = useState(myState.posts)
const makeUpVote = (objIndex) => {
// Make local variable posts to change and set posts while
// using spread operator to make sure we get a new array created instead
// of pointing to the same array in memory
const posts = [...postState]
posts[objIndex].up_vote++
setPostState(posts)
}
let upVote = id => {
// use postState to access instead of myState.posts
let objIndex = postState.findIndex(obj => obj.id == id)
return (
<Fab
// I would recommend creating separate functions to handle this
// instead of writing them inline.
onClick={() => makeUpVote(objIndex)}
></Fab>
)
}
平等检查示例:
// This snippet is just to give you an idea of mutation
const posts = [{id: 1, upvote: 0}, {id: 2, upvote: 0}]
const posts2 = posts
// using spread operator from ES6 to assign a new array with similar values to posts3
const posts3 = [...posts]
posts[0].upvote++
posts3[0].upvote++
// This statement will return true because posts and posts2 have
// the same address in memory (reference) even though we just
// changed posts variable.
// If we set posts2 in state and initial state was posts
// component will NOT re-render
console.log(posts === posts2)
// This will return false because we assigned a new object
// to posts3 using spread operator even though values are same
// If we set posts3 in state an initial state was posts
// component will re-render
console.log(posts === posts3)
// Now another thing to notice is that spread operator does not
// perform deep cloning and therefore the object at index 0 in
// posts has the same reference to object at index 0 in posts 3
// therefore we get upvote = 2
console.log("Posts: ", posts)
console.log("Posts3: ", posts3)
推荐阅读
- c# - 如何调整标签控件的大小
- android - Delphi XE10.2 Tokyo 的 FMX.Platform.Android.pas 与 Delphi XE10.3 Rio 的 FMX.Platform.Android.pas
- android - 在 recyclerview 项目的点击事件上更改 Imageview 在 recyclerview 之外的图像
- php - 为什么 ajax 在 laravel 项目中返回错误响应消息?可能的原因是什么?
- java - 为什么我不能用我写的程序得到没有标记的字符串?
- javascript - 使用带有简单 ssh 的相同 shell 执行多个命令
- python - 如何访问用于调用包含单个 python 文件的 MACOS 应用程序的自定义协议 URL(从 python 代码内部)
- python - wx.lib.agw.ultimatelistctrl。禁止增加元素的高度
- java - kafka宕机时微服务无法启动
- node.js - 使用 Node-Odoo 连接到 Azure 中的 Bitnami Odoo 实例时访问被拒绝错误