尝试使用子组件 2 的数据更新子组件 1 而无需重新渲染子组件 2
Trying to update child component 1 with data from child component 2 without rerender child 2
我正在从一个循环中生成一个沉重的 JSX 数组。
它创造了很多 table。
我想用所选行上的数据更新徽章。但它重新渲染了我所有的 table。更新单个徽章的时间很长。
我尝试使用 useMemo()
来防止创建 table 如果我的数据没有改变,但是来自父级的回调函数没有更新状态。
我正在尝试做的代码示例 =>
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 函数会重新渲染父级,而不是下一次调用(我猜这是因为我没有发送新道具并且函数被复制但没有链接)
也许我的体系结构不好,或者也许有一些解决方案可以更新此 badgeComponent 而无需重新呈现我的所有表。谢谢你的帮助
编辑:
表格组件
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>
)
})
我的表格
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>
);
})
简单表格
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>
);
})
而不是在 App 组件中使用 useMemo,您必须对 TableComponent
使用 React.memo 假设它是一个功能组件或 extend it with React.PureComponent
如果它是 class组件
但是在这种情况下,您必须确保您没有在每个 re-render 上重新创建 updateBadge
函数。为确保这一点,请使用 useCallback
hook
另外不要忘记为从循环中呈现的 TableComponent 实例添加唯一键
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
})
编辑:
根据您的评论代码和框添加工作演示:https://codesandbox.io/s/clever-fast-0cq32
变化不大
- 使用 useCallback 方法并将状态更新程序转换为使用回调方法
- 删除了 JSON.parse 和 JSON.stringify 的逻辑,而是将整个数据存储在数组中。这样做的原因是每次你使用 JSON.parse 它 returns 你一个新的 object 参考,因此备忘录功能在 child 中失败,因为它只是进行参考检查。这将在您的组件 re-render 每次更新徽章状态时发生。
- 如果您仍然需要使用 JSON.parse 和 JSON.stringify,请添加一个自定义比较器来深入比较数据值
我正在从一个循环中生成一个沉重的 JSX 数组。
它创造了很多 table。
我想用所选行上的数据更新徽章。但它重新渲染了我所有的 table。更新单个徽章的时间很长。
我尝试使用 useMemo()
来防止创建 table 如果我的数据没有改变,但是来自父级的回调函数没有更新状态。
我正在尝试做的代码示例 =>
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 函数会重新渲染父级,而不是下一次调用(我猜这是因为我没有发送新道具并且函数被复制但没有链接)
也许我的体系结构不好,或者也许有一些解决方案可以更新此 badgeComponent 而无需重新呈现我的所有表。谢谢你的帮助
编辑:
表格组件
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>
)
})
我的表格
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>
);
})
简单表格
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>
);
})
而不是在 App 组件中使用 useMemo,您必须对 TableComponent
使用 React.memo 假设它是一个功能组件或 extend it with React.PureComponent
如果它是 class组件
但是在这种情况下,您必须确保您没有在每个 re-render 上重新创建 updateBadge
函数。为确保这一点,请使用 useCallback
hook
另外不要忘记为从循环中呈现的 TableComponent 实例添加唯一键
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
})
编辑:
根据您的评论代码和框添加工作演示:https://codesandbox.io/s/clever-fast-0cq32
变化不大
- 使用 useCallback 方法并将状态更新程序转换为使用回调方法
- 删除了 JSON.parse 和 JSON.stringify 的逻辑,而是将整个数据存储在数组中。这样做的原因是每次你使用 JSON.parse 它 returns 你一个新的 object 参考,因此备忘录功能在 child 中失败,因为它只是进行参考检查。这将在您的组件 re-render 每次更新徽章状态时发生。
- 如果您仍然需要使用 JSON.parse 和 JSON.stringify,请添加一个自定义比较器来深入比较数据值