Closed6

「nullish coalescing」と「更新関数」

MAAAAAAAAAAAMAAAAAAAAAAA

nullish coalescingは、??という形で使われます。

この演算子は、左の値がnullまたはundefinedの場合に、右の値を評価して返すことができるものです。もし左の値がnullundefined以外であれば、左の値がそのまま返されます。

例えば、以下のコードを考えてみてください。

const character = {
  name: "Mario"
  // heightが省略されている
};

console.log(character.height ?? '???');  // 出力: '???'

この場合、characterオブジェクトにはheightプロパティがないため、character.heightundefinedと評価されます。したがって、nullish coalescing演算子を使用すると、右の値である'???'が返されます。

三項演算子を使用した場合、このように書くこともできます。

console.log(character.height !== null && character.height !== undefined ? character.height : '???');

しかし、この場合のコードは長くなりますし、nullundefinedのチェックを明示的に行う必要があります。そのため、オプショナルなプロパティが省略されているケースなどには、nullish coalescing演算子を使用する方が読みやすく、効率的です。

MAAAAAAAAAAAMAAAAAAAAAAA

親コンポーネントから子コンポーネントへのデータの受け渡しと、子コンポーネントから親コンポーネントへのフィードバックについてのコード

import type { FC } from 'react';
import { useState } from 'react';
import { Box, Button, ButtonGroup, Stat, StatLabel, StatNumber } from '@chakra-ui/react';

const IncrementButton: FC<{ onClick: () => void }> = ({ onClick }) => (
  <Button w="xs" colorScheme="green" variant="solid" onClick={onClick}>
    +1
  </Button>
);

const ResetButton: FC<{ onClick: () => void }> = ({ onClick }) => (
  <Button w="xs" colorScheme="red" variant="solid" onClick={onClick}>
    Reset
  </Button>
);

const Counter: FC = () => {
  const [count, setCount] = useState(0);
  const increment = () => setCount((c) => c + 1);
  const reset = () => setCount(0);

  return (
    <Box p={5} w="sm" borderWidth="1px" borderRadius="lg" boxShadow="base">
      <Stat mb={2}>
        <StatLabel fontSize={18}>Count</StatLabel>
        <StatNumber fontSize={42}>{count}</StatNumber>
      </Stat>
      <ButtonGroup maxW="xs" m={2} variant="contained" isAttached>
        <IncrementButton onClick={increment} />
        <ResetButton onClick={reset} />
      </ButtonGroup>
    </Box>
  );
};

export default Counter;
MAAAAAAAAAAAMAAAAAAAAAAA

更新関数について

  1. 初めの状態:
const increment = () => setCount((c) => c + 1);

この行は、count の現在の値を参照せずに、以前の値を関数で受け取り、その値に1を加えた新しい値で更新する方法を示しています。

  1. 秋谷さんの予想:
const increment = () => setCount(count + 1);

この書き方は、現在のcountの値に直接アクセスして、1を加える方法を示しています。しかし、この方法はある問題に直面する可能性があります。

  1. 新しい試み:
const plusThree = () => [...Array(3).keys()].forEach(() => setCount(count + 1));

ここで、setCount(count + 1)を3回連続で呼び出していると予想されるコードを導入しています。これにより、ボタンをクリックするとcountが3増加することを期待するかもしれませんが、実際にはそれが起こらない理由があります。

  1. 結果:
    ボタンをクリックしても、countは1しか増加しない。これはReactの特性によるものです。

  2. 解決策の導入:

const plusThree = () => [...Array(3).keys()].forEach(() => setCount((c) => c + 1));

ここで、直接countの現在の値にアクセスするのではなく、関数を使用して前の値を取得してから更新します。これにより、countが正しく3ずつ増加するようになります。

  1. 理由:
    Reactの再レンダリングと状態管理の仕組みにより、setCountが3回連続で呼び出されると、それぞれの呼び出しにおいてcountの値は一定となります。したがって、setCount(count + 1)を使用すると、3回の更新がすべて同じ値に基づいて行われることになります。一方、関数を渡す方法を使用すると、各更新が前回の値に基づいて行われるため、期待される結果が得られます。

この挙動は、Reactの再結合とコンポーネントの差分更新のしくみ、特にコンポーネント関数が実行される際の状態の変更方法に起因しています。

MAAAAAAAAAAAMAAAAAAAAAAA

コンポーネントの副作用について

コンポーネントの『副作用』とは、コンポーネントのレンダリングに影響を及ぼす外部の操作や変更のことを指します。これは、コンポーネントの状態やpropsによって引き起こされるだけでなく、外部データの取得やDOMの手動操作などによっても引き起こされる可能性があります。

以下に具体的な例を示します。

1. ネットワークを介したデータの取得:

ReactのコンポーネントでAPIからデータを取得する場合、その操作は副作用として扱われます。なぜなら、データの取得は非同期であり、その結果に基づいてコンポーネントの状態が変わる可能性があるからです。

import React, { useState, useEffect } from 'react';

function UserComponent() {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetch('/api/user')
      .then(response => response.json())
      .then(data => setUser(data));
  }, []); // 空の依存配列を指定して、コンポーネントのマウント時のみ実行する

  return <div>{user ? user.name : 'Loading...'}</div>;
}

上記のコードでは、useEffectフックを使ってAPIからデータを取得しています。このデータ取得は副作用です。

2. ログの記録:

アプリケーションの動作をログに記録する場合、その操作は副作用として扱われます。

useEffect(() => {
  console.log('Component has been rendered.');
}, []);

3. リアルDOMの手動での書き換え:

直接DOMを操作する場合、その操作は副作用として扱われます。しかし、Reactでは直接DOMを操作するのは推奨されません。

useEffect(() => {
  const titleElement = document.getElementById('title');
  titleElement.style.color = 'blue';
}, []);

以上のように、副作用とはコンポーネントのレンダリングに外部から影響を及ぼす操作のことを指し、useEffectフックを使ってこれらの操作を制御することができます。

MAAAAAAAAAAAMAAAAAAAAAAA

副作用を扱う関数の一つ、useEffectについて

import React, { useState, useEffect, FC } from 'react';

const SampleComponent: FC = () => {
  // 1. Stateの定義
  const [data, setData] = useState<any>(null);

  // その他のロジック...

  // 2. useEffectの使用
  useEffect(() => {
    // 2.1 データのフェッチや設定
    fetchDataFromAPI().then(fetchedData => {
      setData(fetchedData);
    });

    // 2.2 コンポーネントのアンマウント時に実行されるクリーンアップ関数
    return () => {
      clearSomething();
    };
  }, [someDeps]); // 2.3 依存配列

  // レンダリングのロジック...
  return (
    <div>
      {data}
    </div>
  );
}

// 仮のAPIデータのフェッチ関数
const fetchDataFromAPI = async () => {
  // ここで実際にAPIからデータを取得するロジックを書きます。
  return "Sample Data";
}

// 何かのクリーンアップ関数
const clearSomething = () => {
  console.log("Cleanup logic here");
}

export default SampleComponent;

段階的な解説:

  1. Stateの定義:

    • useStateフックを使用して、dataという名前のstateを定義しています。setDataは、このdataの値を更新するための関数です。
  2. useEffectの使用:

    • useEffectは、コンポーネントのライフサイクルに関連した副作用(side effects)を実行するためのフックです。
    • 2.1: ここでは仮のAPIデータ取得関数を使用してデータを取得し、それをstateに設定しています。
    • 2.2: useEffect内でreturnされる関数は、クリーンアップ関数として知られています。この関数は、コンポーネントがアンマウントされる際、または依存配列内の値が変更されたときに実行されます。これは例えばイベントリスナの解除やタイマーのクリアなど、不要な副作用をクリーンアップするために使用します。
    • 2.3: 依存配列[someDeps]は、指定された値に変更があった場合にのみuseEffect内のロジックを再実行するためのものです。もし空の配列[]が渡されると、useEffect内のロジックはコンポーネントのマウント時とアンマウント時にのみ実行されます。

以上のように、このサンプルコンポーネントはuseStateuseEffectフックを中心に、データの取得と表示、そして何らかのクリーンアップロジックを実装しています。

このスクラップは2023/10/30にクローズされました