Как избежать повторного рендеринга после добавления элемента в список?

У меня есть функциональный компонент, который необходимо обновить.

export const VendorCategory = ({setShowProgress, user}) => {
    const classes = useStyles()
    const theme = useTheme()
    const listElements = []
    const [dummyCategory, setDummyCategory] = useState({
        "Test1": [
            "Subtest1", "Subtest2", "Subtest3"
        ],
        "Test2": [
            "Subtest1", "Subtest2", "Subtest3"
        ],
        "Test3": [
            "Subtest1", "Subtest2", "Subtest3"
        ]
    })


    useEffect(() => {
        return () => {
            console.log("re-rendered")
        }
    })
    const AddSubCategory = useCallback(() => {
        setShowProgress(true)
        const newAdd = dummyCategory['Test1']
        newAdd.push("SubTest4")
        setDummyCategory({...dummyCategory, "Test1": newAdd})
        setShowProgress(false)
    },[setShowProgress,setDummyCategory,dummyCategory])



    return (
        <div className={classes.root}>

            <h3 className={classes.title}>Vendor Category</h3>

            <Button
                className={classes.button}
                variant='contained'
                color="primary"
            >
                Add Category
            </Button>
            <div className={classes.categoryDiv}>
                {
                    Object.keys(dummyCategory).map((value, index) => {
                        return (
                            <List>
                            <Accordion key={uuid()} mt={2}>
                                <AccordionSummary
                                    expandIcon={<ExpandMoreIcon/>}
                                    aria-controls="panel1a-content"
                                    id="panel1a-header"
                                >
                                    <ListSubheader key={uuid()} className={classes.sublistHeader} component="div"
                                                   id="nested-list-subheader">
                                        {value}
                                    </ListSubheader>
                                </AccordionSummary>


                                <AccordionDetails className={classes.subCategoryList}>
                                    <Button
                                        className={classes.button}
                                        variant='contained'
                                        color="primary"
                                        onClick={AddSubCategory}
                                    >
                                        Add Sub-Category
                                    </Button>
                                    {dummyCategory[value].map((subvalue, index) => {
                                        return (
                                            <ListItem button key={subvalue}>
                                                <ListItemText inset primary={subvalue}/>
                                            </ListItem>
                                        )
                                    })}

                                </AccordionDetails>
                            </Accordion>
                            </List>)
                    })
                }

            </div>
        </div>
    )
}

const mapStateToProps = (state) => ({
    user: state.user
})

const mapDispatchToProps = {}

export default connect(mapStateToProps, mapDispatchToProps)(VendorCategory)

проблема в том, что аккордеон перерисовывается каждый раз, когда я добавляю к нему элемент, а затем он рушится. как мне предотвратить его разрушение из-за повторного рендеринга? Хук UseCallback также выполняет повторный рендеринг. как мне использовать React.Memo, чтобы избежать этого? Или как мне использовать useEffect в таком случае. Опять же, мне нужно добавить элемент без разрушения аккордеона. Является ли это возможным? Любая помощь будет оценена.


person Kush Singh    schedule 03.02.2021    source источник
comment
Разве у вас нет опции, которую вы могли бы установить на аккордеоне, чтобы определить его состояние (открыто/закрыто)?   -  person Quentin Grisel    schedule 04.02.2021
comment
Если ваш аккордеон рушится, проблема не столько в том, что он перерисовывается, сколько в том, что он перемонтируется. Наиболее вероятная причина в том, что вы не используете стабильные ключи — key={uuid()} будет генерировать новый ключ при каждом рендеринге, поэтому React считает, что это новый компонент. Ключи должны быть уникальными и никогда не меняться в течение всего срока службы компонента.   -  person lawrence-witt    schedule 04.02.2021
comment
Это хорошее предложение ... я попробую и вернусь к вам. :)   -  person Kush Singh    schedule 04.02.2021
comment
Пожалуйста, сделайте вышеуказанное заявление ответом, чтобы я мог поставить вам лайк. Вы спасли меня от многих исследований :)   -  person Kush Singh    schedule 04.02.2021


Ответы (2)


Согласно комментариям, сопоставленным элементам React должны быть предоставлены уникальные, стабильные ключи, которые сохраняются на протяжении всего срока службы элемента. Когда меняется ключ, с точки зрения React, монтируется новый компонент.

Функции, вызываемые встроенными в элемент или компонент, такие как key={uuid()} или onClick={handleClick()}, будут вызываться при каждом рендеринге. В этом случае вызов uuid() inline генерировал новый ключ для каждого рендеринга родительского компонента, вызывая перемонтирование сопоставленных дочерних компонентов.

person lawrence-witt    schedule 04.02.2021

React.memo избегает повторного рендеринга. В вашей функции Accordion попробуйте:

import {memo} from 'react';

const Accordion = (props) => {
    //your code
};

export default memo(Accordion);
person Elson Ramos    schedule 03.02.2021