
大量のCheckboxでチェックの表示が遅くなる
MUIライブラリとはReact用のUIコンポーネントライブラリです。
MUI: The React component library you always wanted
MUIを使用することでMaterial Designに基づくスタイルが簡単に作れて、Reactとの相性も良いのでよく利用されます。
便利なライブラリですが、入力フォームのCheckboxコンポーネントは大量に使用するとチェックボックスを押下した際のチェックのオン・オフの表示が遅くなります。
例えば画面上に2000個のCheckboxコンポーネントを配置してクリックすると、クリックしてからチェックボックスが付くまで時間がかかります。
この表示が遅くなる現象はメモ化やuseStateの分割では解決できません。
App.tsx
import { useState } from 'react'
import { Box, Checkbox, FormControlLabel, Typography } from '@mui/material'
import Grid from '@mui/material/Grid2'
function App() {
const totalCheckboxes = 2000
const [checkedCount, setCheckedCount] = useState(0)
const handleCheckboxChange = (isChecked: boolean) => {
setCheckedCount((prevCount) => prevCount + (isChecked ? 1 : -1))
}
return (
<Box sx={{ padding: 2 }}>
<Typography
variant="h6"
gutterBottom
sx={{ position: 'fixed', bottom: 2, background: 'white', zIndex: 2 }}
>
Checked Count: {checkedCount} / {totalCheckboxes}
</Typography>
<Grid container spacing={2}>
{Array.from({ length: totalCheckboxes }, (_, i) => (
<Grid size={3} key={i}>
<Box
sx={{
padding: 1,
border: '1px solid #ccc',
borderRadius: 4,
textAlign: 'left',
}}
>
<FormControlLabel
control={
<Checkbox
onChange={(e) => handleCheckboxChange(e.target.checked)}
/>
}
label={`Checkbox ${i + 1}`}
/>
</Box>
</Grid>
))}
</Grid>
</Box>
)
}
export default App
MUIのCheckboxコンポーネントを2000個配置したサンプル
※ Checkboxコンポーネントが2000個あるので、Stackbritzだと表示されるのに時間がかかります。
※ 低スペックPCだと500個でもかなり表示が遅くなります。
カスタムCheckboxで表示が遅くなるのを回避する
MUIライブラリは大量のCheckboxでチェックの表示が遅くなる問題は、チェックボックスのコンポーネントを自作で作成して利用することで回避できます。
TSX
import React, { useState } from 'react'
import { Box, Typography } from '@mui/material'
import Grid from '@mui/material/Grid2'
type CustomCheckboxProps = {
checked?: boolean
onChange?: (checked: boolean) => void
label?: string
};
export const CustomCheckbox: React.FC<CustomCheckboxProps> = ({
checked = false,
onChange,
label,
}) => {
const [isChecked, setIsChecked] = useState<boolean>(checked)
const handleToggle = () => {
const newChecked = !isChecked
setIsChecked(newChecked)
if (onChange) {
onChange(newChecked)
}
}
return (
<Box
sx={{
display: 'flex',
alignItems: 'center',
cursor: 'pointer',
userSelect: 'none',
}}
onClick={handleToggle}
>
<Box
sx={{
width: 12,
height: 12,
border: '2px solid #666',
borderRadius: 0.5,
backgroundColor: isChecked ? '#1976d2' : '#fff',
borderColor: isChecked ? '#1976d2' : '#666',
color: '#fff',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
transition: 'background-color 0.3s ease',
fontSize: '0.7rem',
fontWeight: 'bold',
}}
>
{isChecked && <>✓</>}
</Box>
{label && (
<Typography
sx={{ marginLeft: 1, color: '#333', fontSize: 16 }}
component="span"
>
{label}
</Typography>
)}
</Box>
)
}
function App() {
const totalCheckboxes = 1000
const [checkedCount, setCheckedCount] = useState<number>(0)
const handleCheckboxChange = (isChecked: boolean) => {
console.log(isChecked)
setCheckedCount((prevCount) => prevCount + (isChecked ? 1 : -1))
}
return (
<Box sx={{ padding: 2 }}>
<Typography
variant="h6"
gutterBottom
sx={{ position: 'fixed', bottom: 2, background: 'white', zIndex: 2 }}
>
Checked Count: {checkedCount} / {totalCheckboxes}
</Typography>
<Grid container spacing={2}>
{Array.from({ length: totalCheckboxes }, (_, i) => (
<Grid size={3} key={i}>
<Box
sx={{
padding: 1,
border: '1px solid #ccc',
borderRadius: 4,
textAlign: 'left',
}}
>
<CustomCheckbox
onChange={handleCheckboxChange}
label={`Checkbox ${i + 1}`}
/>
</Box>
</Grid>
))}
</Grid>
</Box>
)
}
export default App
自作のCheckboxコンポーネントを2000個配置したサンプル
サンプルのリンク先を確認すると、自作のCheckboxコンポーネントの方はクリックするとすぐにチェック✔が表示されて、軽快に動作することが確認できます。
この自作のCheckboxコンポーネントではinputタグを入れていないので、form内で使用するなどで必要な場合は別途追加してください。