パフォーマンス向上のためのReactコンポーネント再レンダリング
Reactコンポーネントを強制的に再レンダリングする4つの方法
forceUpdate() メソッド
概要: コンポーネントクラスのインスタンスメソッドで、状態に関わらず強制的に再レンダリングを呼び出す。
特徴:
- シンプルで使いやすい
- 状態に関わらず再レンダリングできる
注意点:
- 不要な再レンダリングを招き、パフォーマンス悪化につながる可能性がある
- 非推奨なので、他の方法を優先すべき
コード例:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
};
}
render() {
return (
<div>
カウント: {this.state.count}
<button onClick={() => this.forceUpdate()}>再レンダリング</button>
</div>
);
}
}
ボタンクリックで forceUpdate()
が呼び出され、状態に関わらずコンポーネントが再レンダリングされます。
React.memo() 高階関数
概要: コンポーネントの再レンダリングを最適化する高階関数。
- props が変更された場合のみ再レンダリング
- 不要な再レンダリングを抑制し、パフォーマンス向上
- props の比較は浅い比較
- 深い比較が必要な場合は
useMemo
と組み合わせる
const MyComponent = React.memo(() => {
// ...
});
MyComponent
は React.memo()
でラップされ、props が変更された場合のみ再レンダリングされます。
useRef() Hook
概要: 参照型を作成し、レンダリング間で値を保持する Hook。
- 状態に関わらず値を保持
- 不要な再レンダリングを抑制
- 参照型の更新は直接行う必要がある
const MyComponent = () => {
const countRef = useRef(0);
useEffect(() => {
// ...
}, [countRef.current]);
return (
<div>
カウント: {countRef.current}
<button onClick={() => { countRef.current += 1 }}>カウントアップ</button>
</div>
);
};
countRef
は useRef()
で作成され、レンダリング間でカウント値を保持します。
useReducer() Hook
概要: 状態管理ロジックをカプセル化する Hook。
- 状態更新ロジックを独立させる
- テスト容易性向上
- 複雑な状態管理に適している
const MyComponent = () => {
const [count, dispatch] = useReducer(reducer, initialState);
return (
<div>
カウント: {count}
<button onClick={() => dispatch({ type: 'increment' })}>カウントアップ</button>
</div>
);
};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return state + 1;
default:
return state;
}
}
useReducer
で状態管理ロジックを分離し、状態更新と再レンダリングを明確に分離できます。
4つの方法それぞれの特徴を理解し、状況に応じて適切な方法を選択することが重要です。
forceUpdate() メソッド
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
};
}
render() {
return (
<div>
カウント: {this.state.count}
<button onClick={() => this.forceUpdate()}>再レンダリング</button>
</div>
);
}
}
const App = () => {
return <MyComponent />;
};
ReactDOM.render(<App />, document.getElementById('root'));
React.memo() 高階関数
const MyComponent = React.memo(() => {
const [count, setCount] = useState(0);
return (
<div>
カウント: {count}
<button onClick={() => setCount(count + 1)}>カウントアップ</button>
</div>
);
});
const App = () => {
return <MyComponent />;
};
ReactDOM.render(<App />, document.getElementById('root'));
useRef() Hook
const MyComponent = () => {
const countRef = useRef(0);
useEffect(() => {
console.log('カウント更新:', countRef.current);
}, [countRef.current]);
return (
<div>
カウント: {countRef.current}
<button onClick={() => { countRef.current += 1 }}>カウントアップ</button>
</div>
);
};
const App = () => {
return <MyComponent />;
};
ReactDOM.render(<App />, document.getElementById('root'));
const MyComponent = () => {
const [count, dispatch] = useReducer(reducer, initialState);
return (
<div>
カウント: {count}
<button onClick={() => dispatch({ type: 'increment' })}>カウントアップ</button>
</div>
);
};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return state + 1;
default:
return state;
}
}
const App = () => {
return <MyComponent />;
};
ReactDOM.render(<App />, document.getElementById('root'));
動作確認
各サンプルコードをブラウザで実行し、ボタンをクリックしてコンポーネントの動作を確認してください。
その他のReactコンポーネントを強制的に再レンダリングする方法
key 属性
- キーは一意である必要がある
- 状態や props に依存する値を使用する必要がある場合は注意が必要
const MyComponent = () => {
const items = [1, 2, 3];
return (
<ul>
{items.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
);
};
const App = () => {
return <MyComponent />;
};
ReactDOM.render(<App />, document.getElementById('root'));
上記コードでは、items
配列の各要素に key
属性を指定することで、リストレンダリング時に 不要な再レンダリングを抑制 できます。
useLayoutEffect() Hook
概要: DOM 更新後に実行される Hook。
useEffect()
と似ているが、DOM 更新後に実行される- レイアウト調整などに有効
- パフォーマンスに影響を与える可能性がある
const MyComponent = () => {
const [count, setCount] = useState(0);
useLayoutEffect(() => {
console.log('カウント更新:', count);
}, [count]);
return (
<div>
カウント: {count}
<button onClick={() => setCount(count + 1)}>カウントアップ</button>
</div>
);
};
const App = () => {
return <MyComponent />;
};
ReactDOM.render(<App />, document.getElementById('root'));
上記コードでは、useLayoutEffect
Hook を使用して、DOM 更新後にカウント値を出力 しています。
useImperativeHandle() Hook
概要: 子コンポーネントから親コンポーネントへの参照を取得するための Hook。
- 子コンポーネントの機能を親コンポーネントから制御
- 特殊なケースで有効
- 複雑なコードになりやすい
const MyComponent = () => {
const ref = useRef();
useImperativeHandle(ref, () => ({
focus() {
ref.current.focus();
},
}));
return <input ref={ref} />;
};
const App = () => {
const [focused, setFocused] = useState(false);
const myComponentRef = useRef();
useEffect(() => {
if (focused) {
myComponentRef.current.focus();
}
}, [focused]);
return (
<div>
<input type="checkbox" onChange={(e) => setFocused(e.target.checked)} />
<MyComponent ref={myComponentRef} />
</div>
);
};
ReactDOM.render(<App />, document.getElementById('root'));
上記コードでは、useImperativeHandle
Hook を使用して、子コンポーネントの focus() メソッドを親コンポーネントから呼び出す ことができます。
4つの主要な方法に加えて、状況に応じて上記の3つの方法も検討してください。
reactjs