每当我在任何输入字段中输入内容时,我肯定都会遇到性能问题。这个性能问题就像按住一个键 -> 然后它“暂停”输入字段 -> 然后我停止按住键 -> 然后我可以看到输入值。所以基本上它是滞后。 - 或者用技术的话来说:按下按键开始重新渲染所有其他导致输入延迟的子组件和孙组件。
经过一些分析和 console.logs - 看起来问题出在我的表组件上,该组件正在重新渲染每个按键上的每一行。
我有一个名为 Afrapporter
的主容器,其中我的上下文和 reducer 放置在同一路径中:
const KontrolrapportContext = React.createContext()
const reducer = (state, action) => {
switch (action.type) {
case 'UPDATE_INITIALSTATE': {
// update state
}
case 'HANDLE_CHANGE_TOPSECTION': {
// update state
}
case 'HANDLE_CHANGE_RUBRIKFEJL': {
// update state
}
case 'HANDLE_CHANGE_RUBRIKFEJL_MULTIPLE': {
// update state
}
case 'HANDLE_CHANGE_TOLDRAPPORT': {
// update state
default:
throw new Error(`Unhandled action type: ${action.type}`)
}
}
const Afrapporter = () => {
const kontrolrapport = useSelector(selectKontrolrapport)
const [state, dispatchLocal] = useReducer(reducer, {})
useEffect(() => {
dispatchLocal({ type: 'UPDATE_INITIALSTATE', payload: kontrolrapport })
}, [kontrolrapport])
const dispatch = useDispatch()
let { kontrolrapportId } = useParams()
useEffect(() => {
dispatch(fetchKontrolrapportById(kontrolrapportId))
}, [kontrolrapportId, dispatch])
const value = { state, dispatchLocal }
return (
<>
<KontrolrapportContext.Provider value={value}>
<Grid container justify='center' alignItems='center' spacing={3}>
<Grid container justify='center' spacing={3}>
<Grid item sm={12}>
{state && <TopSection />}
</Grid>
<Grid item xs={12}>
<Typography variant='h5'>Rubrikker</Typography>
{state && <AfrapporteringTable />}
</Grid>
</Grid>
</KontrolrapportContext.Provider>
</>
)
}
}
function useKontrolrapport() {
const context = React.useContext(KontrolrapportContext)
if (context === undefined) {
throw new Error('useKontrolrapport must be used within a KontrolrapportProvider')
}
return context
}
export { Afrapporter, useKontrolrapport }
我的容器有 2 个用户可以与之交互的子组件:TopSection
和 AfrapporteringTable
。
TopSection
是一个包含 10 多个输入字段的表单 - 作为组件提取 - 它使用我的上下文来获取值并设置值。AfrapporteringTable
是一个表格,它显示了我的上下文中的一些数据以及用户可以与之交互的数据。交互式输入类型是选择器。TopSection
为每个输入元素都有一个组件,例如:
const TopSection = () => {
return (
<TableContainer component={Paper}>
<Table size='small'>
<TableBody>
<Referencenummer />
<Varepostnummer />
<Journalnummer />
<Branchekode />
<ToldmaessigAendringOpkraevning />
<ToldmaessigAendringTilbagebetaling />
<FysiskKontrol />
<AfkrydsForOversendelseAfSagTilFinansielAnalyse />
<Ansvarsvurdering />
<Varemodtagernavn />
<VaremodtagerCvr />
<AntagetDato />
</TableBody>
</Table>
</TableContainer>
)
}
export default TopSection
// TopSection childcomponent e.g.:
const Journalnummer = () => {
const { state, dispatchLocal } = useKontrolrapport()
const id = 'workzoneJournalnummer'
const label = 'Journalnummer'
return (
<TableRow>
<TableCell>
<InputLabel>{label}</InputLabel>
</TableCell>
<TableCell>
<TextField
id={id}
type='number'
style={{ margin: 3 }}
fullWidth
margin='normal'
size='small'
value={state.workzoneJournalnummer || ''}
variant='outlined'
onChange={(event) => dispatchLocal({ type: 'HANDLE_CHANGE_TOPSECTION', payload: event })}
/>
</TableCell>
</TableRow>
)
}
export default Journalnummer
这是我的AfrapporteringTable
:`
const Rubrik = {
RUBRIK_TYPE: 'type',
ORIGINAL_VAERDI: 'originalVaerdi',
KORRIGERET_VAERDI: 'korrigeretVaerdi',
MULIGE_FEJL: 'muligeFejl',
}
const RubrikTyper = {
PRAEFERENCE_DOK: 'Præferencedok',
STATISTISK_VAERDI: 'Statistisk værdi',
}
const headers = [
{
id: Rubrik.RUBRIK_TYPE,
label: 'Rubrik',
},
{
id: Rubrik.ORIGINAL_VAERDI,
label: 'Original værdi',
},
{
id: Rubrik.KORRIGERET_VAERDI,
label: 'Korrigeret værdi',
},
{
id: Rubrik.MULIGE_FEJL,
label: 'Mulige fejl',
},
]
const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
PaperProps: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
},
},
};
const useStyles = makeStyles(() => ({
muligeFejlSelect: {
minWidth: 300,
maxWidth: 300,
height: '2.5em',
},
row: {
height: "40px",
},
head: {
backgroundColor: '#14143c',
textAlign:'center',
color: '#ffffff',
minWidth: '200px',
},
cell: {
textAlign:'center',
}
}))
const MuligeFejlMultipleValues = ({
index,
name,
options,
}) => {
const classes = useStyles()
const { state, dispatchLocal } = useKontrolrapport()
const value = state.rubrikker[index]?.valgteFejl.map((item) => item.fejltekstId)
return (
<Select
className={classes.muligeFejlSelect}
name={name}
multiple
MenuProps={MenuProps}
value={value}
onChange={(event) => dispatchLocal({ type: 'HANDLE_CHANGE_RUBRIKFEJL_MULTIPLE', payload: event, index: index})}
variant='outlined'>
<MenuItem value=''>
<em>----</em>
</MenuItem>
{options.length > 0 &&
options.map((option) => (
<MenuItem key={option.fejltekstId} value={option.fejltekstId}>
{option.tekst}
</MenuItem>
))}
</Select>
)
}
const MuligeFejlSingleValue = ({
index,
name,
options,
}) => {
const classes = useStyles()
const { state, dispatchLocal } = useKontrolrapport()
const value = state.rubrikker[index]?.valgteFejl[0]?.fejltekstId
return (
<Select
className={classes.muligeFejlSelect}
name={name}
value={value}
onChange={(event) => dispatchLocal({ type: 'HANDLE_CHANGE_RUBRIKFEJL', payload: event, index: index})}
variant='outlined'>
<MenuItem value=''>
<em>----</em>
</MenuItem>
{options.length > 0 &&
options.map((option) => (
<MenuItem key={option.fejltekstId} value={option.fejltekstId}>
{option.tekst}
</MenuItem>
))}
</Select>
)
}
const SwitchRubrikComponent = ({
index,
rubrikType,
value,
header,
}) => {
const classes = useStyles()
console.log("switch rendered")
switch (header.id) {
case Rubrik.RUBRIK_TYPE: {
return (
<TableCell key={header.id}>
{value}
</TableCell>
)
}
case Rubrik.ORIGINAL_VAERDI:
case Rubrik.KORRIGERET_VAERDI: {
return (
<TableCell className={classes.cell}>
<TextField
key={header.id}
size='small'
fullWidth
value={value}
variant='outlined'
InputProps={{
readOnly: true,
}}
/>
</TableCell>
)
}
case Rubrik.MULIGE_FEJL: {
if (rubrikType === RubrikTyper.PRAEFERENCE_DOK || rubrikType === RubrikTyper.STATISTISK_VAERDI) {
return (
<TableCell className={classes.cell}>
<MuligeFejlMultipleValues
key={header.id}
index={index}
name={rubrikType}
options={value}
/>
</TableCell>
)
}
return (
<TableCell className={classes.cell}>
<MuligeFejlSingleValue
key={header.id}
index={index}
name={rubrikType}
options={value}
/>
</TableCell>
)
}
default:
console.error('Could not find any rubrik with the type: ' + header.id)
return null
}
}
const Header = () => {
const classes = useStyles()
return (
<TableHead>
<TableRow>
{headers.map((header) => (
<TableCell key={header.id} className={classes.head}>
{header.label}
</TableCell>
))}
</TableRow>
</TableHead>
)
}
const Body = () => {
const classes = useStyles()
const { state } = useKontrolrapport()
if (Object.keys(state).length) {
return (
<TableBody>
{state.rubrikker.map((rubrik, index) => {
return (
<TableRow
key={rubrik.rubrikId}
className={classes.row}
hover
role='checkbox'
tabIndex={-1}>
{headers.map((header) => {
const value = rubrik[header.id] || ''
return (
<SwitchRubrikComponent
key={header.id}
index={index}
rubrikType={rubrik.type}
value={value}
header={header}
/>
)
})}
</TableRow>
)
})}
</TableBody>
)
} else {
return null
}
}
const AfrapporteringTable = () => {
return (
<TableContainer component={Paper}>
<Table>
<Header />
<Body />
</Table>
</TableContainer>
)
}
export default AfrapporteringTable
如您所见,我在深度嵌套的子组件中调用 useKontrolrapport()
- 这导致当只有一个组件更改了其值时,其他子组件的不必要重新渲染。如果我理解正确,这是 useContext()
钩子的预期行为。