useState не рендерится, когда функция обновления вызывается внутри функции onClick - PullRequest
3 голосов
/ 10 ноября 2019

Я провел несколько дней, просматривая подобные сообщения без решения. По какой-то причине, когда я использую 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>
    );
}

Любая помощь будет великолепна, спасибо!

1 Ответ

3 голосов
/ 10 ноября 2019

В React компонент повторно визуализируется только при изменении state, т. Е. prevState! == currentState независимо от того, является ли он компонентом класса или функциональным. В вашем случае вы вызываете setPosts, но он не меняет состояние, потому что вы присваиваете тот же объект myState.posts при установке состояния. React не выполняет глубокие проверки на равенство объекта, а просто сравнивает ссылку на объект в состоянии. В вашем случае ссылка никогда не меняется, так как вы мутируете, и prevState остается равным newState после вызова setPosts.

Чтобы избежать этой проблемы, при установке состояния с помощью Objects / Arrays в реагировании вам необходимоубедитесь, что вы назначаете новую ссылку. Таким образом, сравнение prevState и currState возвращает ложь. См. Пример проверок на равенство для более подробной информации

Правильный способ доступа и установки состояния:

// 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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...