react study 5週目

9774 ワード

1.ライフサイクル


ComponentDidMount

  • 素子を初めて運転するときに
  • を運転する.
  • setStateなどとしてレンダリングされた場合は実行されません
  • は主に非同期要求
  • である.

    ComponentWillUnmount

  • 要素を除去する前に
  • を実行する.
  • 親部品が子部品を除去する場合も
  • を実行する.
  • 非同期要求をクリアする(コンポーネントDidMountまたはコンポーネントDidUpdateからの非同期要求がまだ存在する場合、メモリリークなどの問題が発生する可能性があるため、コンポーネントWinUnMountからクリアする)
  • .

    ComponentDidUpdate

  • レンダリング後に
  • を実行

    shouldComponentUpdate

  • が変化する場合にのみ、
  • がレンダリングされます.

    2.石切り布ゲーム(class)

    import React, { Component } from 'react';
    
    const rspCoords = {
      바위: '0',
      가위: '-142px',
      보: '-284px',
    };
    
    const scores = {
      가위: 1,
      바위: 0,
      보: -1,
    };
    
    const computerChoice = (imgCoord) => { // computer가 어떤 손을 냈는지 판단
      return Object.entries(rspCoords).find(function(v) {
        return v[1] === imgCoord;
      })[0];
    };
    
    class RSP extends Component { 
      state = {
        result: '',
        imgCoord: '0',
        score: 0,
      }
    
      interval;
    
      // 라이프사이클
      // constructor -> render -> ref -> ComponentDidMount -> 
      // (setState/props 바뀔 때 -> shouldComponentUpdate -> render -> componentDidUpdate)
      // 부모가 나를 없앴을 때 -> componentWillUnmount -> 소멸
    
      componentDidMount() { // 컴포넌트 첫 렌더링 후 비동기 요청
        this.interval = setInterval(this.changeHand, 100)
        // 최초 1번만 실행.
        // 주로 비동기 요청
      }
    
      componentWillUnmount() {
        // 컴포넌트가 제거되기 직전
        // 비동기 요청 정리
        clearInterval(this.interval);
      }
      
      changeHand = () => {
          const {imgCoord} = this.state;
          if (imgCoord === rspCoords.바위) {
            this.setState({
              imgCoord: rspCoords.가위,
            });   
          } else if (imgCoord === rspCoords.가위) {
            this.setState({
              imgCoord: rspCoords.보,
            });
          }
          else if (imgCoord === rspCoords.보) {
            this.setState({
              imgCoord: rspCoords.바위,
            });
          }
      }
    
      onClickBtn = (choice) => () => {
        const {imgCoord} = this.state;
    
        clearInterval(this.interval);
        const myScore = scores[choice];
        const cpuScore = scores[computerChoice(imgCoord)];
        const diff = myScore - cpuScore;
        if (diff === 0) {
          this.setState({
            result: '비겼습니다!',
          }) 
        } else if ([-1, 2].includes(diff)) {
          this.setState((prevState) => {
            return {
              result: '이겼습니다!',
              score: prevState.score + 1,
            }
          });
        } else {
          this.setState ((prevState) => {
            return {
              result: '졌습니다!',
              score: prevState.score -1,
            };
          });
        }
        setTimeout(() => {
          this.interval = setInterval(this.changeHand);
        }, 1000);
        
      };
    
      render() { 
        const { result, score, imgCoord } = this.state;
        // imgCoord : 이미지의 좌표
          return(
            <>
            <div id="computer" style={{ background: `url(https://en.pimg.jp/023/182/267/1/23182267.jpg) ${imgCoord} 0` }} />
            <div>
              <button id="rock" className="btn" onClick={this.onClickBtn('바위')}>바위</button>
              <button id="scissor" className="btn" onClick={this.onClickBtn('가위')}>가위</button>
              <button id="paper" className="btn" onClick={this.onClickBtn('보')}>보</button>
            </div>
            <div>{result}</div>
            <div>현재 {score}점</div>
          </>
        );
      }
    }
    
    export default RSP;

    3.じゃんけんゲーム(Hooks)


    Hooksにはライフサイクルはありませんが、UserEffectを使用して同様の機能を実現できます.
    この場合、userEffectはレンダリング結果を反映した後に呼び出されます.
    import React, { useState, useRef, useEffect } from 'react';
    
    const rspCoords = {
      바위: '0',
      가위: '-142px',
      보: '-284px',
    };
    
    const scores = {
      가위: 1,
      바위: 0,
      보: -1,
    };
    
    const computerChoice = (imgCoord) => { // computer가 어떤 손을 냈는지 판단
      return Object.entries(rspCoords).find(function(v) {
        return v[1] === imgCoord;
      })[0];
    };
    
    const RSP = () => {
      const [result, setResult] = useState('');
      const [imgCoord, setimgCoord] = useState(rspCoords.바위);
      const [score, setScore] = useState(0);
      const interval = useRef();
    
      useEffect(() => {
        // compoentDidMount, componentDidUpdate 역할 (1:1 대응은 X)
        interval.current = setInterval(changeHand, 100);
        return () => {
          // componentWillUnmount 역할
          clearInterval(interval.current);
        }
      }, [imgCoord]); // useEffect를 실행하고 싶은 state. 여러 개 참조도 가능 
      // 하지만 꼭 useEffect를 다시 실행할 값만 넣어야 함
      // imgCoord가 바뀔 때마다 useEffect가 계속 실행됨
    
      const changeHand = () => {
          if (imgCoord === rspCoords.바위) {
            setimgCoord(rspCoords.가위); 
          } else if (imgCoord === rspCoords.가위) {
            setimgCoord(rspCoords.보); 
          }
          else if (imgCoord === rspCoords.보) {
            setimgCoord(rspCoords.바위); 
          }
      }
    
      const onClickBtn = (choice) => () => {
        // if (interval.current) {
          
        // }
        clearInterval(interval.current);
        const myScore = scores[choice];
        const cpuScore = scores[computerChoice(imgCoord)];
        const diff = myScore - cpuScore;
    
        if (diff === 0) {
          setResult('비겼습니다!');
        } else if ([-1, 2].includes(diff)) {
          setResult('이겼습니다!');
          setScore((prevScore) => prevScore + 1);
        } else {
          setResult('졌습니다!');
          setScore((prevScore) => prevScore - 1);
        }
        setTimeout(() => {
          interval.current = setInterval(changeHand);
        }, 1000);
      };
    
        // imgCoord : 이미지의 좌표
          return(
            <>
            <div id="computer" style={{ background: `url(https://en.pimg.jp/023/182/267/1/23182267.jpg) ${imgCoord} 0` }} />
            <div>
              <button id="rock" className="btn" onClick={onClickBtn('바위')}>바위</button>
              <button id="scissor" className="btn" onClick={onClickBtn('가위')}>가위</button>
              <button id="paper" className="btn" onClick={onClickBtn('보')}>보</button>
            </div>
            <div>{result}</div>
            <div>현재 {score}점</div>
          </>
        );
    }

    4.classとHooksのライフサイクルを比較する

  • クラスでは、コンポーネントDidMountまたはコンポーネントDidUpdateの未知の状態を条件文として分岐処理します.
  • Hooksの場合、userEffectはstateを1つずつ処理する.
  • 追加


  • Cannot update during an existing state transition (such as within 'render'). Render methods should be a pure function of props and state. Uncaught Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
    これは.
    onClickBtn = (choice) => {
    
    {...}
    
    }
    書き間違えた.😣 (キャプチャコードが正しい)
    呼び出し関数->render->呼び出し関数->render...このように無限ループに陥って生じるエラー.

    このコードをchangeHand()に書いたときも同様のエラーが発生しました.