useReducer は、γƒͺデγƒ₯γƒΌγ‚΅ (reducer) γ‚’γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ«θΏ½εŠ γ™γ‚‹γŸγ‚οΏ½? React フックです。

const [state, dispatch] = useReducer(reducer, initialArg, init?)

γƒͺフゑレンス

useReducer(reducer, initialArg, init?)

γƒͺデγƒ₯γƒΌγ‚΅ で state γ‚’οΏ½?οΏ½η†γ™γ‚‹γŸγ‚γ«γ€γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆοΏ½?γƒˆγƒƒγƒ—γƒ¬γƒ™γƒ«γ§ useReducer を呼び出します。

import { useReducer } from 'react';

function reducer(state, action) {
// ...
}

function MyComponent() {
const [state, dispatch] = useReducer(reducer, { age: 42 });
// ...

さらに例を見る

εΌ•ζ•°

  • reducer: state をど�?γ‚ˆγ†γ«ζ›΄ζ–°γ™γ‚‹γ‹γ‚’ζŒ‡οΏ½?�するγƒͺデγƒ₯ーァ閒数です。純粋でγͺγ‘γ‚Œγ°γͺγ‚‰γšγ€εΌ•ζ•°γ¨γ—γ¦ state γ¨γ‚’γ‚―γ‚·γƒ§γƒ³γ‚’ε–γ‚Šγ€ζ¬‘οΏ½? state を返します。state とをクションはど�?γ‚ˆγ†γͺεž‹γ§γ‚‚ε€§δΈˆε€«γ§γ™γ€‚
  • initialArg: 初期 state が計�?οΏ½γ•γ‚Œγ‚‹ε…ƒγ«γͺる倀です。任意�?εž‹οΏ½?ε€€γ‚’ζŒ‡οΏ½?�できます。ど�?γ‚ˆγ†γ«εˆζœŸ state γ‚’θ¨ˆοΏ½?�するかは、欑�? init εΌ•ζ•°γ«δΎε­˜γ—γΎγ™γ€‚
  • 省η•₯可能 init: 初期 state γ‚’θΏ”γ™εˆζœŸεŒ–ι–’ζ•°γ§γ™γ€‚ζŒ‡οΏ½?οΏ½γ•γ‚Œγ¦γ„γͺγ„ε ΄εˆγ€εˆζœŸ state は initialArg そ�?γ‚‚οΏ½?にγͺγ‚ŠγΎγ™γ€‚ζŒ‡οΏ½?οΏ½γ•γ‚Œγ¦γ„γ‚‹ε ΄εˆγ€εˆζœŸ state は init(initialArg) οΏ½?硐果にγͺγ‚ŠγΎγ™γ€‚

θΏ”γ‚Šε€€

useReducer は、2 ぀�?ε€€γ‚’ζŒγ€ι…εˆ—γ‚’θΏ”γ—γΎγ™οΌš

  1. 現在�? stateγ€‚ζœ€εˆοΏ½?レンダー中に、init(initialArg) または initialArg(init がγͺγ„ε ΄εˆοΌ‰γŒθ¨­οΏ½?οΏ½γ•γ‚ŒγΎγ™γ€‚
  2. state γ‚’εˆ₯οΏ½?ε€€γ«ζ›΄ζ–°γ—γ€ε†γƒ¬γƒ³γƒ€γƒΌγ‚’γƒˆγƒͺγ‚¬γ™γ‚‹γŸγ‚οΏ½? dispatch 閒数。

注意点

  • useReducer はフックγͺοΏ½?γ§γ€γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆοΏ½?γƒˆγƒƒγƒ—γƒ¬γƒ™γƒ«γΎγŸγ―η‹¬θ‡ͺοΏ½?カスタムフック内で�?γΏε‘Όγ³ε‡Ίγ™γ“γ¨γŒγ§γγΎγ™γ€‚γƒ«γƒΌγƒ—γ‚„ζ‘δ»ΆοΏ½?中で呼び出すことはできません。必要γͺε ΄εˆγ―γ€ζ–°γ—γ„γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ¨γ—γ¦ζŠœγε‡Ίγ—γ€γοΏ½?中に state を移動させてください。
  • Strict Mode では、React は純粋でγͺγ„ι–’ζ•°γ‚’θ¦‹γ€γ‘γ‚„γ™γγ™γ‚‹γŸγ‚γ«γ€γƒͺデγƒ₯γƒΌγ‚΅γ¨εˆζœŸεŒ–ι–’ζ•°γ‚’ 2 ε›žε‘Όγ³ε‡Ίγ—γΎγ™γ€‚γ“γ‚Œγ―ι–‹η™Ίζ™‚οΏ½?ε‹•δ½œγ§γ‚γ‚Šγ€ζœ¬η•ͺγ«γ―ε½±ιŸΏγ—γΎγ›γ‚“γ€‚γƒͺデγƒ₯γƒΌγ‚΅γ¨εˆζœŸεŒ–ι–’ζ•°γŒη΄”η²‹γ§γ‚γ‚Œγ°οΌˆγγ†γ‚γ‚‹γΉγγ§γ™οΌ‰γ€γ“γ‚Œγ―γƒ­γ‚Έγƒƒγ‚―γ«ε½±ιŸΏγ—γΎγ›γ‚“γ€‚η‰‡ζ–ΉοΏ½?呼び出し�?η΅ζžœγ―η„‘θ¦–γ•γ‚ŒγΎγ™γ€‚

dispatch ι–’ζ•°

useReducer γ«γ‚ˆγ£γ¦θΏ”γ•γ‚Œγ‚‹ dispatch 閒数を使うことで、state γ‚’εˆ₯οΏ½?ε€€γ«ζ›΄ζ–°γ—γ¦ε†γƒ¬γƒ³γƒ€γƒΌγ‚’γƒˆγƒͺγ‚¬γ™γ‚‹γ“γ¨γŒγ§γγΎγ™γ€‚dispatch 閒数には、唯一�?εΌ•ζ•°γ¨γ—γ¦γ‚’γ‚―γ‚·γƒ§γƒ³γ‚’ζΈ‘γ™εΏ…θ¦γŒγ‚γ‚ŠγΎγ™γ€‚

const [state, dispatch] = useReducer(reducer, { age: 42 });

function handleClick() {
dispatch({ type: 'incremented_age' });
// ...

React γŒγ‚»γƒƒγƒˆγ™γ‚‹ζ¬‘οΏ½? state は、引数として state οΏ½?ηΎεœ¨ε€€γŠγ‚ˆγ³ dispatch γ«ζΈ‘γ•γ‚ŒγŸγ‚’γ‚―γ‚·γƒ§γƒ³γ‚’η”¨γ„γ¦ reducer ι–’ζ•°γ‚’ε‘Όγ³ε‡Ίγ—γŸη΅ζžœγ«γͺγ‚ŠγΎγ™γ€‚

εΌ•ζ•°

  • action: γƒ¦γƒΌγ‚Άγ«γ‚ˆγ£γ¦οΏ½?οΏ½θ‘Œγ•γ‚ŒγŸγ‚’γ‚―γ‚·γƒ§γƒ³γ§γ™γ€‚δ»»ζ„οΏ½?εž‹οΏ½?ε€€γ‚’ζŒ‡οΏ½?οΏ½γ§γγΎγ™γ€‚ζ…£δΎ‹γ¨γ—γ¦γ€γ‚’γ‚―γ‚·γƒ§γƒ³γ―ι€šεΈΈγ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγ§γ‚γ‚Šγ€type γƒ—γƒ­γƒ‘γƒ†γ‚£γ§θ­˜εˆ₯γ•γ‚Œγ€δ»–οΏ½?プロパティでγ‚ͺプション�?θΏ½εŠ ζƒ…ε ±γ‚’δΏζŒγ—γΎγ™γ€‚

θΏ”γ‚Šε€€

dispatch ι–’ζ•°γ«γ―θΏ”γ‚Šε€€γ―γ‚γ‚ŠγΎγ›γ‚“γ€‚

注意点

  • dispatch 閒数は、欑�?レンダー�?γŸγ‚οΏ½? state 倉数�?みを更新します。dispatch ι–’ζ•°γ‚’ε‘Όγ³ε‡Ίγ—γŸεΎŒγ« state 倉数をθͺ­γΏε–γ‚‹γ¨γ€ζ—’γ«η”»ι’δΈŠγ«θ‘¨η€Ίγ•γ‚Œγ¦γ„γ‚‹γ€dispatch 呼び出し前�?叀い倀をθͺ­γΏε–γ‚ŠγΎγ™γ€‚

  • δΈŽγˆγ‚‰γ‚ŒγŸζ–°γ—γ„ε€€γŒγ€Object.is οΏ½?ζ―”θΌƒγ«γ‚ˆγ‚Šγ€ηΎεœ¨οΏ½? state γ¨εŒγ˜γ¨εˆ€ζ–­γ•γ‚ŒγŸε ΄εˆγ€React γ―γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ¨γοΏ½?子要素�?ε†γƒ¬γƒ³γƒ€γƒΌγ‚’γ‚Ήγ‚­γƒƒγƒ—γ—γΎγ™γ€‚γ“γ‚Œγ―ζœ€ι©εŒ–γ§γ™γ€‚React γ―η΅ζžœγ‚’η„‘θ¦–γ§γγ‚‹ε ΄εˆγ§γ‚‚εΏ…θ¦γŒγ‚γ£γ¦γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ‚’ε‘Όγ³ε‡Ίγ™γ‹γ‚‚γ—γ‚ŒγΎγ›γ‚“γŒγ€γ“γ‚ŒγŒγ‚³γƒΌγƒ‰γ«ε½±ιŸΏγ—γ¦γ―γ„γ‘γΎγ›γ‚“γ€‚

  • React は state οΏ½?ζ›΄ζ–°οΏ½?γƒγƒƒγƒε‡¦η†οΌˆbatching, ζŸγ­ε‡¦η†οΌ‰ γ‚’θ‘Œγ„γΎγ™γ€‚γ“γ‚Œγ«γ‚ˆγ‚Šγ€γ™γΉγ¦οΏ½?γ‚€γƒ™γƒ³γƒˆγƒγƒ³γƒ‰γƒ©γŒοΏ½?οΏ½θ‘Œγ•γ‚Œγ€γγ‚Œγ‚‰οΏ½? set ι–’ζ•°γŒε‘Όγ³ε‡Ίγ•γ‚ŒγŸεΎŒγ«η”»ι’γŒζ›΄ζ–°γ•γ‚ŒγΎγ™γ€‚γ“γ‚Œγ«γ‚ˆγ‚Šγ€1 ぀�?γ‚€γƒ™γƒ³γƒˆδΈ­γ«θ€‡ζ•°ε›žοΏ½?ε†γƒ¬γƒ³γƒ€γƒΌγŒη™Ίη”Ÿγ™γ‚‹οΏ½?γ‚’ι˜²γγ“γ¨γŒγ§γγΎγ™γ€‚η¨€γͺケースとして、DOM にをクセスするγͺど�?理由で React γ«η”»ι’γ‚’ζ—©ζœŸγ«εΌ·εˆΆηš„γ«ζ›΄ζ–°γ•γ›γ‚‹εΏ…θ¦γŒγ‚γ‚‹ε ΄εˆγ―γ€flushSync を使用できます。


使用法

γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ«γƒͺデγƒ₯γƒΌγ‚΅γ‚’θΏ½εŠ γ™γ‚‹

γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆοΏ½?γƒˆγƒƒγƒ—γƒ¬γƒ™γƒ«γ§ useReducer を呼び出して、γƒͺデγƒ₯ーァを使って state γ‚’οΏ½?�理します。

import { useReducer } from 'react';

function reducer(state, action) {
// ...
}

function MyComponent() {
const [state, dispatch] = useReducer(reducer, { age: 42 });
// ...

useReducer は、2 ぀�?ε€€γ‚’ζŒγ€ι…εˆ—γ‚’θΏ”γ—γΎγ™οΌš

  1. こ�? state 倉数�?現在�? stateγ€‚δΈŽγˆγ‚‰γ‚ŒγŸεˆζœŸ state γŒεˆζœŸε€€γ¨γ—γ¦θ¨­οΏ½?οΏ½γ•γ‚ŒγΎγ™γ€‚
  2. γƒ¦γƒΌγ‚Άζ“δ½œγ«εΏœγ˜γ¦ state γ‚’ε€‰ζ›΄γ™γ‚‹γŸγ‚οΏ½?dispatch 閒数。

η”»ι’δΈŠοΏ½?ε†…οΏ½?οΏ½γ‚’ζ›΄ζ–°γ™γ‚‹γ«γ―γ€γ‚’γ‚―γ‚·γƒ§γƒ³γ¨ε‘Όγ°γ‚Œγ‚‹γ€γƒ¦γƒΌγ‚ΆγŒθ‘Œγ£γŸγ“γ¨γ‚’θ‘¨γ™γ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγ‚’εΌ•ζ•°γ¨γ—γ¦ dispatch を呼び出します。

function handleClick() {
dispatch({ type: 'incremented_age' });
}

React は現在�? state とをクションをγƒͺデγƒ₯ーァ閒数に渑します。γƒͺデγƒ₯ーァは欑�? state γ‚’θ¨ˆοΏ½?�して返します。React はそ�?欑�? state γ‚’δΏε­˜γ™γ‚‹γ¨γ¨γ‚‚γ«γ€γοΏ½? state γ‚’δ½Ώγ£γ¦γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ‚’γƒ¬γƒ³γƒ€γƒΌγ—γ€UI を更新します。

import { useReducer } from 'react';

function reducer(state, action) {
  if (action.type === 'incremented_age') {
    return {
      age: state.age + 1
    };
  }
  throw Error('Unknown action.');
}

export default function Counter() {
  const [state, dispatch] = useReducer(reducer, { age: 42 });

  return (
    <>
      <button onClick={() => {
        dispatch({ type: 'incremented_age' })
      }}>
        Increment age
      </button>
      <p>Hello! You are {state.age}.</p>
    </>
  );
}

useReducer は useState γ¨ιžεΈΈγ«δΌΌγ¦γ„γΎγ™γŒγ€γ‚€γƒ™γƒ³γƒˆγƒγƒ³γƒ‰γƒ©γ‹γ‚‰ state ζ›΄ζ–°γƒ­γ‚Έγƒƒγ‚―γ‚’γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆοΏ½?ε€–οΏ½?ε˜δΈ€οΏ½?ι–’ζ•°γ«η§»ε‹•γ™γ‚‹γ“γ¨γŒγ§γγΎγ™γ€‚θ©³γ—γγ―γ€useState と useReducer οΏ½?選び方を参照ください。


γƒͺデγƒ₯γƒΌγ‚΅ι–’ζ•°οΏ½?書き方

γƒͺデγƒ₯ーァ閒数は欑�?γ‚ˆγ†γ«οΏ½?οΏ½θ¨€γ•γ‚ŒγΎγ™οΌš

function reducer(state, action) {
// ...
}

そ�?εΎŒγ€ζ¬‘οΏ½? state γ‚’θ¨ˆοΏ½?�して返すコードを中に書いていきます。慣例として、switch ζ–‡γ¨γ—γ¦ζ›Έγγ“γ¨γŒδΈ€θˆ¬ηš„γ§γ™γ€‚switch οΏ½?各 case ごとに、欑�? state γ‚’θ¨ˆοΏ½?�して返します。

function reducer(state, action) {
switch (action.type) {
case 'incremented_age': {
return {
name: state.name,
age: state.age + 1
};
}
case 'changed_name': {
return {
name: action.nextName,
age: state.age
};
}
}
throw Error('Unknown action: ' + action.type);
}

をクション�?归はど�?γ‚ˆγ†γͺγ‚‚οΏ½?γ§γ‚‚ζ§‹γ„γΎγ›γ‚“γ€‚ζ…£δΎ‹γ¨γ—γ¦γ€γ‚’γ‚―γ‚·γƒ§γƒ³γ‚’θ­˜εˆ₯γ™γ‚‹γŸγ‚οΏ½? type γƒ—γƒ­γƒ‘γƒ†γ‚£γ‚’ζŒγ£γŸγ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγ‚’ζΈ‘γ™γ“γ¨γŒδΈ€θˆ¬ηš„γ§γ™γ€‚γ‚’γ‚―γ‚·γƒ§γƒ³γ«γ―γƒͺデγƒ₯γƒΌγ‚΅γŒζ¬‘οΏ½? state γ‚’θ¨ˆοΏ½?οΏ½γ™γ‚‹γŸγ‚γ«εΏ…θ¦γͺζœ€ε°ι™οΏ½?情報を含めるべきです。

function Form() {
const [state, dispatch] = useReducer(reducer, { name: 'Taylor', age: 42 });

function handleButtonClick() {
dispatch({ type: 'incremented_age' });
}

function handleInputChange(e) {
dispatch({
type: 'changed_name',
nextName: e.target.value
});
}
// ...

をクション�?γ‚Ώγ‚€γƒ—εγ―γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆε†…γ«γƒ­γƒΌγ‚«γƒ«γͺγ‚‚οΏ½?です。各をクションは、耇数�?データ�?倉更に぀γͺγŒγ‚‹γ‚‚οΏ½?γ§γ‚γ£γ¦γ‚‚γ€ε˜δΈ€οΏ½?γƒ¦γƒΌγ‚Άζ“δ½œγ‚’θ‘¨γ™γ‚ˆγ†γ«γ—γ¦γγ γ•γ„γ€‚state οΏ½?ε½’γ―δ»»ζ„γ§γ™γŒγ€ι€šεΈΈγ―γ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγΎγŸγ―ι…εˆ—γ«γͺγ‚ŠγΎγ™γ€‚

詳しくは、γƒͺデγƒ₯ーァへ�? state ロジック�?ζŠ½ε‡Ίγ‚’ε‚η…§γγ γ•γ„γ€‚

落とし穴

state はθͺ­γΏε–γ‚Šε°‚η”¨γ§γ™γ€‚state ε†…οΏ½?γ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγ‚„ι…εˆ—γ‚’ε€‰ζ›΄γ—γͺいでください。

function reducer(state, action) {
switch (action.type) {
case 'incremented_age': {
// 🚩 Don't mutate an object in state like this:
state.age = state.age + 1;
return state;
}

δ»£γ‚γ‚Šγ«γ€εΈΈγ«ζ–°γ—γ„γ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγ‚’γƒͺデγƒ₯ーァから返してください。

function reducer(state, action) {
switch (action.type) {
case 'incremented_age': {
// βœ… Instead, return a new object
return {
...state,
age: state.age + 1
};
}

詳しくは、state ε†…οΏ½?γ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆοΏ½?更新と state ε†…οΏ½?ι…εˆ—οΏ½?更新を参照ください。

εŸΊζœ¬ηš„γͺ useReducer οΏ½?δΎ‹

δΎ‹ 1/3:
γƒ•γ‚©γƒΌγƒ οΌˆγ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆοΌ‰

こ�?例では、γƒͺデγƒ₯γƒΌγ‚΅γŒ 2 ぀�?γƒ•γ‚£γƒΌγƒ«γƒ‰οΌˆname と ageοΌ‰γ‚’ζŒγ€ state γ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγ‚’οΏ½?�理しています。

import { useReducer } from 'react';

function reducer(state, action) {
  switch (action.type) {
    case 'incremented_age': {
      return {
        name: state.name,
        age: state.age + 1
      };
    }
    case 'changed_name': {
      return {
        name: action.nextName,
        age: state.age
      };
    }
  }
  throw Error('Unknown action: ' + action.type);
}

const initialState = { name: 'Taylor', age: 42 };

export default function Form() {
  const [state, dispatch] = useReducer(reducer, initialState);

  function handleButtonClick() {
    dispatch({ type: 'incremented_age' });
  }

  function handleInputChange(e) {
    dispatch({
      type: 'changed_name',
      nextName: e.target.value
    }); 
  }

  return (
    <>
      <input
        value={state.name}
        onChange={handleInputChange}
      />
      <button onClick={handleButtonClick}>
        Increment age
      </button>
      <p>Hello, {state.name}. You are {state.age}.</p>
    </>
  );
}


初期 state οΏ½?ε†δ½œζˆγ‚’ιΏγ‘γ‚‹

React は初期 state γ‚’δΈ€εΊ¦δΏε­˜γ—γŸοΏ½?γ‘γ€ζ¬‘ε›žδ»₯降�?レンダーでは焑視します。

function createInitialState(username) {
// ...
}

function TodoList({ username }) {
const [state, dispatch] = useReducer(reducer, createInitialState(username));
// ...

createInitialState(username) οΏ½?η΅ζžœγ―εˆζœŸγƒ¬γƒ³γƒ€γƒΌγ§οΏ½?γΏδ½Ώη”¨γ•γ‚ŒγΎγ™γŒγ€γƒ¬γƒ³γƒ€γƒΌοΏ½?εΊ¦γ«ζ―Žε›žγ“οΏ½?ι–’ζ•°γ‚’ε‘Όγ³ε‡Ίγ—γ¦γ„γΎγ™γ€‚γ“γ‚Œγ―γ€ε€§γγͺι…εˆ—γ‚’δ½œζˆγ—γŸγ‚Šγ€ι«˜γ‚³γ‚Ήγƒˆγͺ計�?οΏ½γ‚’θ‘Œγ£γŸγ‚Šγ—γ¦γ„γ‚‹ε ΄εˆγ«η„‘ι§„γ«γͺγ‚‹ε―θƒ½ζ€§γŒγ‚γ‚ŠγΎγ™γ€‚

γ“γ‚Œγ‚’θ§£ζ±Ίγ™γ‚‹γŸγ‚γ«γ€useReducer οΏ½? 3 η•ͺοΏ½?οΏ½?εΌ•ζ•°γ¨γ—γ¦εˆζœŸεŒ–ι–’ζ•°γ‚’ζΈ‘γ™γ“γ¨γŒγ§γγΎγ™οΌš

function createInitialState(username) {
// ...
}

function TodoList({ username }) {
const [state, dispatch] = useReducer(reducer, username, createInitialState);
// ...

createInitialState γ‚’ζΈ‘γ—γ¦γ„γ‚‹γ“γ¨γ«ζ³¨ζ„γ—γ¦γγ γ•γ„γ€‚γ“γ‚Œγ―ι–’ζ•°γοΏ½?γ‚‚οΏ½?γ§γ‚γ‚Šγ€createInitialState() γ§γ―γ‚γ‚ŠγΎγ›γ‚“γ€‚γ“γ‚Œγ«γ‚ˆγ‚Šγ€εˆζœŸ state γ―εˆζœŸεŒ–εΎŒγ«ε†δ½œζˆγ•γ‚Œγͺくγͺγ‚ŠγΎγ™γ€‚

上記�?例では、createInitialState は username εΌ•ζ•°γ‚’ε—γ‘ε–γ‚ŠγΎγ™γ€‚εˆζœŸεŒ–ι–’ζ•°γŒεˆζœŸ state γ‚’θ¨ˆοΏ½?οΏ½γ™γ‚‹γŸγ‚γ«ζƒ…ε ±γ‚’εΏ…θ¦γ¨γ—γͺγ„ε ΄εˆγ―γ€useReducer に対して 2 η•ͺοΏ½?οΏ½?引数として null γ‚’ζΈ‘γ™γ“γ¨γŒγ§γγΎγ™γ€‚

εˆζœŸεŒ–ι–’ζ•°γ¨εˆζœŸ state γ‚’η›΄ζŽ₯渑す方法�?違い

δΎ‹ 1/2:
εˆζœŸεŒ–ι–’ζ•°γ‚’ζΈ‘γ™

こ�?δΎ‹γ§γ―γ€εˆζœŸεŒ–ι–’ζ•°γ‚’ζΈ‘γ—γ¦γ„γ‚‹γŸγ‚γ€createInitialState ι–’ζ•°γ―εˆζœŸεŒ–ζ™‚γ«οΏ½?み�?οΏ½θ‘Œγ•γ‚ŒγΎγ™γ€‚εˆζœŸεŒ–ι–’ζ•°γ―ε…₯εŠ›γƒ•γ‚£γƒΌγƒ«γƒ‰γ«ζ–‡ε­—γ‚’ε…₯εŠ›γ™γ‚‹γͺγ©γ§γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγŒε†γƒ¬γƒ³γƒ€γƒΌγ•γ‚ŒγŸγ¨γ—γ¦γ‚‚οΏ½?οΏ½θ‘Œγ•γ‚ŒγΎγ›γ‚“γ€‚

import { useReducer } from 'react';

function createInitialState(username) {
  const initialTodos = [];
  for (let i = 0; i < 50; i++) {
    initialTodos.push({
      id: i,
      text: username + "'s task #" + (i + 1)
    });
  }
  return {
    draft: '',
    todos: initialTodos,
  };
}

function reducer(state, action) {
  switch (action.type) {
    case 'changed_draft': {
      return {
        draft: action.nextDraft,
        todos: state.todos,
      };
    };
    case 'added_todo': {
      return {
        draft: '',
        todos: [{
          id: state.todos.length,
          text: state.draft
        }, ...state.todos]
      }
    }
  }
  throw Error('Unknown action: ' + action.type);
}

export default function TodoList({ username }) {
  const [state, dispatch] = useReducer(
    reducer,
    username,
    createInitialState
  );
  return (
    <>
      <input
        value={state.draft}
        onChange={e => {
          dispatch({
            type: 'changed_draft',
            nextDraft: e.target.value
          })
        }}
      />
      <button onClick={() => {
        dispatch({ type: 'added_todo' });
      }}>Add</button>
      <ul>
        {state.todos.map(item => (
          <li key={item.id}>
            {item.text}
          </li>
        ))}
      </ul>
    </>
  );
}


γƒˆγƒ©γƒ–γƒ«γ‚·γƒ₯ーティング

γ‚’γ‚―γ‚·γƒ§γƒ³γ‚’γƒ‡γ‚£γ‚Ήγƒ‘γƒƒγƒγ—γŸγŒγƒ­γ‚°γ«γ―ε€γ„ state οΏ½?ε€€γŒθ‘¨η€Ίγ•γ‚Œγ‚‹

dispatch 閒数を呼び出しても、�?�葌中�?コード内�? state γ―ε€‰ζ›΄γ•γ‚ŒγΎγ›γ‚“οΌš

function handleClick() {
console.log(state.age); // 42

dispatch({ type: 'incremented_age' }); // Request a re-render with 43
console.log(state.age); // Still 42!

setTimeout(() => {
console.log(state.age); // Also 42!
}, 5000);
}

γ“γ‚Œγ― state γŒγ‚ΉγƒŠγƒƒγƒ—γ‚·γƒ§γƒƒγƒˆοΏ½?γ‚ˆγ†γ«ζŒ―γ‚‹θˆžγ†γŸγ‚γ§γ™γ€‚state を更新すると、新しい state οΏ½?ε€€γ§ε†γƒ¬γƒ³γƒ€γƒΌγŒθ¦ζ±‚γ•γ‚ŒγΎγ™γŒγ€ζ—’γ«οΏ½?�葌中�?γ‚€γƒ™γƒ³γƒˆγƒγƒ³γƒ‰γƒ©ε†…οΏ½? state JavaScript ε€‰ζ•°γ«γ―ε½±ιŸΏγ‚’δΈŽγˆγΎγ›γ‚“γ€‚

欑�? state οΏ½?ε€€γ‚’ζŽ¨ζΈ¬γ™γ‚‹εΏ…θ¦γŒγ‚γ‚‹ε ΄εˆγ―γ€γƒͺデγƒ₯γƒΌγ‚΅γ‚’θ‡ͺεˆ†γ§ε‘Όγ³ε‡Ίγ™γ“γ¨γ§ζ‰‹ε‹•γ§θ¨ˆοΏ½?οΏ½γ™γ‚‹γ“γ¨γŒγ§γγΎγ™οΌš

const action = { type: 'incremented_age' };
dispatch(action);

const nextState = reducer(state, action);
console.log(state); // { age: 42 }
console.log(nextState); // { age: 43 }

γ‚’γ‚―γ‚·γƒ§γƒ³γ‚’γƒ‡γ‚£γ‚Ήγƒ‘γƒƒγƒγ—γŸγŒη”»ι’γŒζ›΄ζ–°γ•γ‚Œγͺい

React は、Object.is οΏ½?ζ―”θΌƒγ«γ‚ˆγ‚Šγ€ζ¬‘οΏ½? state γŒε‰οΏ½? state γ¨η­‰γ—γ„γ¨εˆ€ζ–­γ•γ‚Œγ‚‹ε ΄εˆγ€ζ›΄ζ–°γ‚’η„‘θ¦–γ—γΎγ™γ€‚γ“γ‚Œγ―ι€šεΈΈγ€state ε†…οΏ½?γ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγ‚„ι…εˆ—γ‚’η›΄ζŽ₯ζ›Έγζ›γˆγŸε ΄εˆγ«η™Ίη”Ÿγ—γΎγ™οΌš

function reducer(state, action) {
switch (action.type) {
case 'incremented_age': {
// 🚩 Wrong: mutating existing object
state.age++;
return state;
}
case 'changed_name': {
// 🚩 Wrong: mutating existing object
state.name = action.nextName;
return state;
}
// ...
}
}

ζ—’ε­˜οΏ½? state γ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγ‚’ζ›Έγζ›γˆγ¦θΏ”γ—γ¦γ„γ‚‹γŸγ‚γ€React γ―ζ›΄ζ–°γ‚’η„‘θ¦–γ—γΎγ™γ€‚γ“γ‚Œγ‚’οΏ½?正するには、常に state ε†…οΏ½?γ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆοΏ½?ζ›΄ζ–°γ‚„ state ε†…οΏ½?ι…εˆ—οΏ½?ζ›΄ζ–°γ‚’ζ­£γ—γθ‘Œγ„γ€γ“γ‚Œγ‚‰οΏ½?ζ›Έγζ›γˆγ‚’ιΏγ‘γ‚‹εΏ…θ¦γŒγ‚γ‚ŠγΎγ™γ€‚

function reducer(state, action) {
switch (action.type) {
case 'incremented_age': {
// βœ… Correct: creating a new object
return {
...state,
age: state.age + 1
};
}
case 'changed_name': {
// βœ… Correct: creating a new object
return {
...state,
name: action.nextName
};
}
// ...
}
}

γƒ‡γ‚£γ‚Ήγƒ‘γƒƒγƒεΎŒγ«γƒͺデγƒ₯ーァから�? state οΏ½?δΈ€ιƒ¨γŒ undefined にγͺγ‚‹

新しい state γ‚’θΏ”γ™ιš›γ«γ€γ™γΉγ¦οΏ½? case οΏ½?εˆ†ε²γ«γŠγ„γ¦γ™γΉγ¦οΏ½?ζ—’ε­˜οΏ½?γƒ•γ‚£γƒΌγƒ«γƒ‰γ‚’γ‚³γƒ”γƒΌγ™γ‚‹γ‚ˆγ†γ«γͺっているか璺θͺγ—γ¦γγ γ•γ„οΌš

function reducer(state, action) {
switch (action.type) {
case 'incremented_age': {
return {
...state, // Don't forget this!
age: state.age + 1
};
}
// ...

上記�? ...state γ‚’ηœη•₯γ™γ‚‹γ¨γ€θΏ”γ•γ‚Œγ‚‹ζ¬‘οΏ½? state には age フィールド�?γΏγŒε«γΎγ‚Œγ€δ»–οΏ½?γƒ•γ‚£γƒΌγƒ«γƒ‰γ―δ½•γ‚‚ε«γΎγ‚Œγͺくγͺってしまいます。


γƒ‡γ‚£γ‚Ήγƒ‘γƒƒγƒεΎŒγ«γƒͺデγƒ₯ーァから�? state ε…¨δ½“γŒ undefined にγͺγ‚‹

state γŒδΊˆζœŸγ›γš undefined にγͺγ‚‹ε ΄εˆγ€γŠγγ‚‰γγ„γšγ‚Œγ‹οΏ½? case で state γ‚’ return γ—εΏ˜γ‚Œγ¦γ„γ‚‹γ‹γ€γ‚’γ‚―γ‚·γƒ§γƒ³οΏ½?γ‚Ώγ‚€γƒ—γŒγ„γšγ‚ŒοΏ½? case とも一致していγͺγ„γ‹γ‚‰γ§γ™γ€‚εŽŸε› γ‚’θ¦‹γ€γ‘γ‚‹γŸγ‚γ«γ€switch οΏ½?倖でエラーをスローしてみましょう。

function reducer(state, action) {
switch (action.type) {
case 'incremented_age': {
// ...
}
case 'edited_name': {
// ...
}
}
throw Error('Unknown action: ' + action.type);
}

γΎγŸγ€γ“οΏ½?γ‚ˆγ†γͺγƒŸγ‚Ήγ‚’γ‚­γƒ£γƒƒγƒγ™γ‚‹γŸγ‚γ«γ€TypeScript οΏ½?γ‚ˆγ†γͺι™ηš„εž‹γƒγ‚§γƒƒγ‚«γ‚’δ½Ώη”¨γ™γ‚‹γ“γ¨γ‚‚γ§γγΎγ™γ€‚


β€œToo many re-renders” γ¨γ„γ†γ‚¨γƒ©γƒΌγŒη™Ίη”Ÿγ™γ‚‹

Too many re-renders. React limits the number of renders to prevent an infinite loop. γ¨γ„γ†γ‚¨γƒ©γƒΌγŒθ‘¨η€Ίγ•γ‚Œγ‚‹γ“γ¨γŒγ‚γ‚ŠγΎγ™γ€‚ι€šεΈΈγ€γ“γ‚Œγ―γƒ¬γƒ³γƒ€γƒΌδΈ­γ«γ‚’γ‚―γ‚·γƒ§γƒ³γ‚’η„‘ζ‘δ»Άγ§γƒ‡γ‚£γ‚Ήγƒ‘γƒƒγƒγ—γ¦γ„γ‚‹γŸγ‚γ€γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγŒγƒ«γƒΌγƒ—γ«ε…₯γ£γ¦γ„γ‚‹γ“γ¨γ‚’ζ„ε‘³γ—γΎγ™οΌšγƒ¬γƒ³γƒ€γƒΌγ€γƒ‡γ‚£γ‚Ήγƒ‘γƒƒγƒοΌˆγ“γ‚Œγ«γ‚ˆγ‚Šε†εΊ¦γƒ¬γƒ³γƒ€γƒΌγŒη™Ίη”ŸοΌ‰γ€γƒ¬γƒ³γƒ€γƒΌγ€γƒ‡γ‚£γ‚Ήγƒ‘γƒƒγƒοΌˆγ“γ‚Œγ«γ‚ˆγ‚Šε†εΊ¦γƒ¬γƒ³γƒ€γƒΌγŒη™Ίη”ŸοΌ‰γ€γ¨γ„γ£γŸηΉ°γ‚ŠθΏ”γ—γ§γ™γ€‚ιžεΈΈγ«ε€šγοΏ½?ε ΄εˆγ€γ“γ‚Œγ―γ‚€γƒ™γƒ³γƒˆγƒγƒ³γƒ‰γƒ©οΏ½?ζŒ‡οΏ½?�方法�?γƒŸγ‚Ήγ«γ‚ˆγ£γ¦εΌ•γθ΅·γ“γ•γ‚ŒγΎγ™γ€‚

// 🚩 Wrong: calls the handler during render
return <button onClick={handleClick()}>Click me</button>

// βœ… Correct: passes down the event handler
return <button onClick={handleClick}>Click me</button>

// βœ… Correct: passes down an inline function
return <button onClick={(e) => handleClick(e)}>Click me</button>

こ�?エラー�?εŽŸε› γŒγ‚γ‹γ‚‰γͺγ„ε ΄εˆγ―γ€γ‚³γƒ³γ‚½γƒΌγƒ«οΏ½?エラー�?ζ¨ͺγ«γ‚γ‚‹ηŸ’ε°γ‚’γ‚―γƒͺックし、JavaScript スタックをθͺΏγΉγ¦γ‚¨γƒ©γƒΌοΏ½?εŽŸε› γ¨γͺγ‚‹ε…·δ½“ηš„γͺ dispatch 閒数呼び出しを見぀けてください。


γƒͺデγƒ₯γƒΌγ‚΅γΎγŸγ―εˆζœŸεŒ–ι–’ζ•°γŒ 2 ε›žοΏ½?οΏ½θ‘Œγ•γ‚Œγ‚‹

Strict Mode では、React はγƒͺデγƒ₯γƒΌγ‚΅γ¨εˆζœŸεŒ–ι–’ζ•°γ‚’ 2 ε›žε‘Όγ³ε‡Ίγ—γΎγ™γ€‚γ“γ‚Œγ«γ‚ˆγ£γ¦γ‚³γƒΌγƒ‰γŒε£Šγ‚Œγ‚‹γ“γ¨γŒγ‚γ£γ¦γ―γͺγ‚ŠγΎγ›γ‚“γ€‚

こ�?開発専用�?ζŒ―γ‚‹θˆžγ„γ―γ€γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ‚’η΄”η²‹γ«δΏγ€γŸγ‚γ«ε½Ήη«‹γ‘γΎγ™γ€‚React は 2 ε›žοΏ½?呼び出し�?うけ�? 1 ぀�?η΅ζžœγ‚’δ½Ώη”¨γ—γ€γ‚‚γ† 1 ぀�?η΅ζžœγ―η„‘θ¦–γ—γΎγ™γ€‚γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ€γ‚€γƒ‹γ‚·γƒ£γƒ©γ‚€γ‚Άγ€γŠγ‚ˆγ³γƒͺデγƒ₯γƒΌγ‚΅ι–’ζ•°γŒη΄”η²‹γ§γ‚γ‚‹ι™γ‚Šγ€γ“γ‚Œγ―γƒ­γ‚Έγƒƒγ‚―γ«ε½±ιŸΏγ‚’δΈŽγˆγΎγ›γ‚“γ€‚γŸγ γ—γ€γ“γ‚Œγ‚‰οΏ½?ι–’ζ•°γŒθͺ€γ£γ¦γ„γ¦δΈη΄”γ§γ‚γ‚‹ε ΄εˆγ€γ“γ‚Œγ«γ‚ˆγ‚ŠγƒŸγ‚Ήγ«ζ°—δ»˜γγ“γ¨γŒγ§γγΎγ™γ€‚

δΎ‹γˆγ°γ€δ»₯δΈ‹οΏ½?純粋でγͺいγƒͺデγƒ₯γƒΌγ‚΅ι–’ζ•°γ―γ‚Ήγƒ†γƒΌγƒˆε†…οΏ½?ι…εˆ—γ‚’ζ›Έγζ›γˆγ¦γ„γΎγ™οΌš

function reducer(state, action) {
switch (action.type) {
case 'added_todo': {
// 🚩 Mistake: mutating state
state.todos.push({ id: nextId++, text: action.text });
return state;
}
// ...
}
}

React がγƒͺデγƒ₯γƒΌγ‚΅ι–’ζ•°γ‚’ 2 ε›žε‘Όγ³ε‡Ίγ™γŸγ‚γ€todo が 2 ε›žθΏ½εŠ γ•γ‚ŒγŸγ“γ¨γŒγ‚γ‹γ‚Šγ€γƒŸγ‚ΉγŒγ‚γ‚‹γ“γ¨γŒγ‚γ‹γ‚ŠγΎγ™γ€‚γ“οΏ½?δΎ‹γ§γ―γ€ι…εˆ—οΏ½?ζ›Έγζ›γˆγ§γ―γͺく�?γζ›γˆγ‚’θ‘Œγ†γ“γ¨γ§γƒŸγ‚Ήγ‚’οΏ½?ζ­£γ§γγΎγ™οΌš

function reducer(state, action) {
switch (action.type) {
case 'added_todo': {
// βœ… Correct: replacing with new state
return {
...state,
todos: [
...state.todos,
{ id: nextId++, text: action.text }
]
};
}
// ...
}
}

γ“γ‚Œγ§γƒͺデγƒ₯ーァ閒数は純粋にγͺγ£γŸγŸγ‚γ€1 ε›žδ½™εˆ†γ«ε‘Όγ³ε‡Ίγ•γ‚Œγ¦γ‚‚ε‹•δ½œγ«ε½±ιŸΏγ―γ‚γ‚ŠγΎγ›γ‚“γ€‚γ“γ‚ŒγŒ React が 2 ε›žε‘Όγ³ε‡Ίγ™γ“γ¨γ§γƒŸγ‚Ήγ‚’η™Ίθ¦‹γ§γγ‚‹η†η”±γ§γ™γ€‚γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ€εˆζœŸεŒ–ι–’ζ•°γ€γŠγ‚ˆγ³γƒͺデγƒ₯γƒΌγ‚΅ι–’ζ•°οΏ½?γΏγŒη΄”η²‹γ§γ‚γ‚‹εΏ…θ¦γŒγ‚γ‚ŠγΎγ™γ€‚γ‚€γƒ™γƒ³γƒˆγƒγƒ³γƒ‰γƒ©γ―η΄”η²‹γ§γ‚γ‚‹εΏ…θ¦γ―γͺγ„γŸγ‚γ€React γ―γ‚€γƒ™γƒ³γƒˆγƒγƒ³γƒ‰γƒ©γ‚’ 2 ε›žε‘Όγ³ε‡Ίγ™γ“γ¨γ―γ‚γ‚ŠγΎγ›γ‚“γ€‚

θ©³η΄°γ―γ€γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ‚’η΄”η²‹γ«δΏγ€γ‚’ε‚η…§γ—γ¦γγ γ•γ„γ€‚