Closed4
Reactのレンダリングについてメモ
こういうコードだとstateが変わるたびにInputとButtonが再レンダリングされる。
親コンポーネント(InputButton)が再レンダリングされるので正しい挙動だけど無駄なはず🤔
InputButton.tsx
import { Button } from '../../../components/button/Button';
import { Input } from '../../../components/input/Input';
import { useDispatch } from 'react-redux';
import { ChangeEvent, FormEvent, useState } from 'react';
import { addList } from '../../../store';
export const InputButton = () => {
console.log('in InputButton');
const dispatch = useDispatch();
const [name, setName] = useState<string | undefined>();
const changeName = (event: ChangeEvent<HTMLInputElement>) => {
console.log('changeName: ', name);
setName(event.target.value);
}
const [age, setAge] = useState<number | undefined>();
const changeAge = (event: ChangeEvent<HTMLInputElement>) => {
console.log('changeAge: ', age);
setAge(Number(event.target.value) || 0);
}
const buttonClick = (event: FormEvent<HTMLButtonElement>) => {
console.log('buttonClick');
console.log(`name: ${name} / age: ${age}`);
if (!name || !age) {
return;
}
dispatch(addList({name, age}));
}
return (<>
<label> name:
<Input type='text' onChange={changeName}/>
</label>
<label> age:
<Input type='number' onChange={changeAge}/>
</label>
<Button text='追加' onClick={buttonClick}/>
</>)
}
InputとButtonをメモ化してみる
してみるがこれではダメ。
親コンポーネントに引きづられて再レンダリングされる。
Input.tsx
import { ChangeEvent, useMemo } from "react";
type InputProps = {
type: 'text' | 'number',
placeHolder?: string,
onChange?: (event: ChangeEvent<HTMLInputElement>) => void
};
export const Input: React.FC<InputProps> = ({type, placeHolder, onChange}) => {
return useMemo(() => {
console.log(`in Input. type is ${type}`);
return <input type={type} placeholder={placeHolder} onChange={onChange}/>
}, [type, placeHolder, onChange]);
}
Button.tsx
import { FC, FormEvent, useMemo } from 'react';
import './button.css';
type ButtonProps = {
text: string;
onClick?: (event: FormEvent<HTMLButtonElement>) => void;
}
export const Button: FC<ButtonProps> = (props) => {
return useMemo(() => {
console.log(`in Button. text is ${props.text}`);
return <button onClick={props.onClick}>{props.text}</button>;
}, [props]);
}
渡している関数もメモ化する
ここまでやってInput
については無駄なレンダリングがなくなる。
Button
はname
とage
が変更される度にbuttonClick
が更新されるので、この書き方しても無駄。
InputButton.tsx
import { Button } from '../../../components/button/Button';
import { Input } from '../../../components/input/Input';
import { useDispatch } from 'react-redux';
import { ChangeEvent, FormEvent, useCallback, useState } from 'react';
import { addList } from '../../../store';
export const InputButton = () => {
console.log('in InputButton');
const dispatch = useDispatch();
const [name, setName] = useState<string | undefined>();
const changeName = useCallback((event: ChangeEvent<HTMLInputElement>) => {
console.log('changeName');
setName(event.target.value);
}, []);
// ここから
const [age, setAge] = useState<number | undefined>();
const changeAge = useCallback((event: ChangeEvent<HTMLInputElement>) => {
console.log('changeAge');
setAge(Number(event.target.value) || 0);
}, []);
const buttonClick = useCallback((event: FormEvent<HTMLButtonElement>) => {
console.log('buttonClick');
console.log(`name: ${name} / age: ${age}`);
if (!name || !age) {
return;
}
dispatch(addList({name, age}));
}, [name, age]);
// ここまでを変更
return (<>
<label> name:
<Input type='text' onChange={changeName}/>
</label>
<label> age:
<Input type='number' onChange={changeAge}/>
</label>
<Button text='追加' onClick={buttonClick}/>
</>)
}
このスクラップは2020/12/21にクローズされました