Попытка обновить дочерний компонент 1 данными из дочернего компонента 2 без переопределения дочернего компонента 2 - PullRequest
1 голос
/ 10 апреля 2020

Я создаю тяжелый массив JSX из всех oop. Это создает много таблицы. Я хотел бы обновить значок с данными по выбранной строке. Но это перерисовывает все мои таблицы. Это довольно долго для обновления одного значка. Я пытался использовать useMemo(), чтобы предотвратить создание таблицы, если мои данные не меняются, но функция обратного вызова от родителя не обновляет состояние.

Пример кода, который я пытаюсь сделать =>

function App() {
    const [tableData, setTableData] = useState(null)
    const [badgeData, setBadgeData] = useState(null)
    const jsx = useMemo(() => createTable(tableData), [tableData])

    function updateBadge(selectedRows) {
        setBadgeData(addNewRow(selectedRows))
    }


    function createTable(data) {
        let jsx = []
        data.forEach((item) => {
            jsx.push(<TableComponent data={data.var} onRowSelect={updateBadge}/>)
        })
        return jsx;
    }

    return (
        <div>
            <HandleDataGeneration setData={setTableData}/>
            <MyBadgeComponent data={badgeData}/>
            {jsx}
        </div>
    );
}

В этом случае только первый вызов функции updateBadge переопределяет родительский элемент, но не вызовы nexts (я думаю, это потому, что я не отправляю новые реквизиты, а функция копируется и не связано)

Может быть, моя архитектура плохая, или, может быть, есть какое-то решение для обновления этого badgeComponent без перерисовки всех моих таблиц. Спасибо за вашу помощь

РЕДАКТИРОВАТЬ:

TableComponent

const TableCompoennt = React.memo((props) => { // after edit but it was a classic fn
    const classes = useStyles();
    const [expanded, setExpanded] = useState(props.data ? `panel${props.i}` : false);
    let disabled = false;

    const handleChange = (panel) => (event, isExpanded) => {
        setExpanded(isExpanded ? panel : false);
    };
    if (isNaN(props.data.var)) {
        props.data.var = x
    }
    if (!props.data)
        disabled = true;
    return (
        <ExpansionPanel TransitionProps={{ unmountOnExit: false }} expanded={expanded === `panel${props.i}`} onChange={handleChange(`panel${props.i}`)} disabled={disabled}>
            <ExpansionPanelSummary
                expandIcon={<ExpandMoreIcon/>}
                aria-controls="panel1bh-content"
                id="panel1bh-header"
            >
                <Tooltip title={props.data.var}>
                <Typography className={classes.heading}>{props.data.var}-{props.data.var}</Typography>
                </Tooltip>
                {!disabled ?
                    <Typography
                        className={classes.secondaryHeading}>{expanded ? "click to hide data" : "click to display data"}</Typography> :
                    <Typography
                        className={classes.secondaryHeading}>no data</Typography>
                }
            </ExpansionPanelSummary>
            <ExpansionPanelDetails>
                <MyTable data={props.data} var={props.var}
                         key={props.i} id={props.i}  style={{width: "100%"}} updateBadge={props.updateBadge}/>
            </ExpansionPanelDetails>
        </ExpansionPanel>
    )
})

MyTable

export default React.memo((props) => { // same here
    const [open, setOpen] = useState(false);
    const [rowData, setRowData] = useState(null);
    const [rows, setRows] = useState(props.myRates);

    calcTotal(rows);
    useEffect(() => {
        setRows(props.myRates)
    }, [props]);

    return (
        <div style={{width: "100%"}}>
            {(rows && rows.length) &&
                <div style={{width: "100%"}}>
                    <Modal open={open} rowData={rowData} setRowData={setRowData}
                                setOpen={(value) => setOpen(value)}/>
                    <Paper style={{height: 400, width: '100%'}}>
                    <SimpleTable
                        columns={columns}
                        rows={rows}
                        handleRowClick={(row) =>{
                                setOpen(true);
                                setRowData(row);
                        }}
                        handleSelect={(row) => {
                            if (!row.selected)
                                row.selected = false;
                            row.selected = !row.selected;
                            props.updateBadge(row)
                    }}
                    />
                    </Paper>
                </div>}
        </div>
    );
})

SimpleTable

const SimpleTable = React.memo((props) =>  {
    const classes = useStyles();
    let dataLabel = generateLabel(props.columns);

    function getRowData(row) {
        props.handleRowClick(row);
    }

    return (
        <TableContainer component={Paper}>
            <Table className={classes.table} aria-label="simple table">
                <TableHead>
                    <TableRow>
                        {dataLabel}
                    </TableRow>
                </TableHead>
                <TableBody>
                    {props.rows.map((row) => (
                        <TableRow key={row.unique_code} selected={row.selected} hover onClick={() => {getRowData(row)}}>
                            {generateRow(props.columns, row)}
                            <TableCell onClick={(event) => {
                                event.stopPropagation();
                            }
                            }>
                                <Checkbox onClick={(event) => {
                                    event.stopPropagation();
                                    props.handleSelect(row);
                                }}
                                />
                            </TableCell>
                        </TableRow>
                    ))}

                </TableBody>
            </Table>
        </TableContainer>
    );
})

1 Ответ

0 голосов
/ 10 апреля 2020

Вместо использования useMemo внутри компонента приложения вы должны использовать React.memo для TableComponent, предполагая, что это функциональный компонент, или extend it with React.PureComponent, если это компонент класса

Однако в таком случае , вы должны убедиться, что вы не воссоздаете функцию updateBadge при каждом повторном рендеринге. Чтобы убедиться в этом, используйте useCallback hook

Также не забудьте добавить уникальный ключ к экземплярам TableComponent, которые отображаются из l oop

function App() {

    const [tableData, setTableData] = useState(null)
    const [badgeData, setBadgeData] = useState(null)

    const updateBadge = useCallback((selectedRows) {
        setBadgeData(addNewRow(selectedRows))
    }, []);

    return (
        <div>
            <HandleDataGeneration setData={setTableData}/>
            <MyBadgeComponent data={badgeData}/>
            {data.map((item) => {
                return <TableComponent key={item.id} props={item} onRowSelect={updateBadge}/>)
             })}
        </div>
    );

}

и в TableComponent

const TableComponent = React.memo((props) => {
 // Rest of the TableComponent code
})

РЕДАКТИРОВАТЬ:

Добавление рабочей демонстрации на основе ваших кодов комментариев box: https://codesandbox.io/s/clever-fast-0cq32

Несколько изменений

  • , созданный с использованием метода useCallback, а также преобразовавший средство обновления состояния для использования метода обратного вызова
  • Удалены логи c для JSON .parse и JSON .stringify и вместо этого сохранены все данные в массиве. Причина этого в том, что каждый раз, когда вы используете JSON .parse, он возвращает вам новую ссылку на объект, и, следовательно, функциональность памятки в дочернем сбое, так как он просто проверяет ссылку. Это будет происходить каждый раз при повторном рендеринге вашего компонента, т.е. при обновлении состояния значка.
  • , если вам все еще нужно использовать JSON .parse и JSON .stringify, добавьте пользовательский компаратор, который сравнивает значения данных глубоко
...