Open3

reactまとめ

kanekingkaneking

再レンダリングに関与する値はすべてリアクティブな値といえる

props, component内で宣言された変数, state

ロジックにもリアクティブなものとそうでないものがある

例えば、send(message)はリアクティブ出ないといえる。なぜなら、messageはユーザーの入力によって変わるリアクティブな値(state)であるが、状態の変化をトリガーに送信を実行したいわけではないからである。そして、このようにリアクティブでないロジックに関してはイベントハンドラで行うべきである。リアクティブなロジックに関しては、useEffectで同期させる。
リアクティブなロジックというと、chatroomに接続するようなロジックなどである。なぜならroomIdなどが変われば、それに応じて接続するroomが変わりこれはリアクティブに実行されるべきロジックだからである。

つまり、ロジックが受け取る引数がリアクティブであっても、そのロジックが実行されるタイミングがリアクティブ出ない場合はそのロジックはイベントハンドラで行うべきであって、リアクティブである場合はエフェクトで処理を実装する。

kanekingkaneking

不要なエフェクトのパターン

具体的な例に入る前に。エフェクトでstateを更新すると、再計算が余分に起きるので非効率かつ可読性も下がるので基本的には避けられるのなら避ける。
あと、僕も含めあるあるなのが、stateやpropsから何かしらの値を計算する場合useeffectが依存配列の変化でトリガーされるので、effectを使いがちだけど、そもそもstateが変わればそのコンポーネントとその子の再レンダリング走るんだから普通に計算しろ

  1. propsまたはstateに基づいてstateを更新する例
function Form() {
  const [firstName, setFirstName] = useState('Taylor');
  const [lastName, setLastName] = useState('Swift');

  // 🔴 Avoid: redundant state and unnecessary Effect
  const [fullName, setFullName] = useState('');
  useEffect(() => {
    setFullName(firstName + ' ' + lastName);
  }, [firstName, lastName]);
  // ...
}
function Form() {
  const [firstName, setFirstName] = useState('Taylor');
  const [lastName, setLastName] = useState('Swift');
  // ✅ Good: calculated during rendering
  const fullName = firstName + ' ' + lastName;
  // ...
}

stateが変われば再計算が走るので、コンポーネントのトップレベルで普通に計算すればよい。

  1. 重たい計算のキャッシュ
function TodoList({ todos, filter }) {
  const [newTodo, setNewTodo] = useState('');

  // 🔴 Avoid: redundant state and unnecessary Effect
  const [visibleTodos, setVisibleTodos] = useState([]);
  useEffect(() => {
    setVisibleTodos(getFilteredTodos(todos, filter));
  }, [todos, filter]);

  // ...
}
function TodoList({ todos, filter }) {
  const [newTodo, setNewTodo] = useState('');
  // ✅ This is fine if getFilteredTodos() is not slow.
  const visibleTodos = getFilteredTodos(todos, filter);
  // ...
}

これも普通に状態にしなくて変数で計算すればいい。何度も言わせるなよ?stateが変わったら再計算走るのだろうが。

まあ、以下のようにnewTodoの変更のたびに実行されるのが、getFilterdTodosの処理に1sぐらいかかるとかで嫌なんだったら、useMemo

import { useMemo, useState } from 'react';

function TodoList({ todos, filter }) {
  const [newTodo, setNewTodo] = useState('');
  // ✅ Does not re-run getFilteredTodos() unless todos or filter change
  const visibleTodos = useMemo(() => getFilteredTodos(todos, filter), [todos, filter]);
  // ...
}
kanekingkaneking

state

  1. stateの評価値を定数として定義することで、複数のstate管理を避けることができる

messageを送るコンポーネントについて考える

export default function Form() {
    const [text, setText] = useState('');
    const [isSending, setIsSending] = useState('');
    const [isSent, setIsSent] = useState('');

    async function handleSumbmit(e) {
        // 省略
}

    if (isSent) {
        return <h1> thanks for feedback!</h1>
        
}

     return (
    <form onSubmit={handleSubmit}>
      <p>How was your stay at The Prancing Pony?</p>
      <textarea
        disabled={isSending}
        value={text}
        onChange={e => setText(e.target.value)}
      />
      <br />
      <button
        disabled={isSending}
        type="submit"
      >
        Send
      </button>
      {isSending && <p>Sending...</p>}
    </form>
  );
} 

 

以上のようにすると
isSending, isSentがともにtrueであるというありえない場合の余地を残してします

statusというstateに置き換えて評価値を定数に格納して管理する

exprot default function FeedbackForm() {
    const [text, setText] = useState('');
    const [status, setStatus] = useState('typing');

    const isSending = status === 'sending';
      const isSent = status === 'sent';

    if (isSent) {
    return <h1>Thanks for feedback!</h1>
  }

  return (
    <form onSubmit={handleSubmit}>
      <p>How was your stay at The Prancing Pony?</p>
      <textarea
        disabled={isSending}
        value={text}
        onChange={e => setText(e.target.value)}
      />
      <br />
      <button
        disabled={isSending}
        type="submit"
      >
        Send
      </button>
      {isSending && <p>Sending...</p>}
    </form>
  );
}

}

  1. 冗長なstateを避ける

before

    const [firstName, setFirstName] = useState('');
    const [lastName, setLastName] = useState('');
    const [fullName, setFullName] = useState('');

after

    const [firstName, setFirstName] = useState('');
    const [lastName, setLastName] = useState('');
    const fullName = firstName + ' ' + lastName;