React Hooks 일반 장면의 사용(소결)

18975 단어 ReactHooks장면
앞말
React는 v16.8 버전에서 React Hooks의 새로운 기능을 제공합니다.내가 보기에 React Hooks를 사용하는 것은 이전의 클래스 구성 요소에 비해 다음과 같은 몇 가지 장점이 있다.
  • 코드의 가독성이 더욱 강하고 원래 같은 기능의 코드 논리는 서로 다른 생명주기 함수에 분리되어 개발자로 하여금 유지보수와 교체에 불리하게 하기 쉽다. React Hooks를 통해 기능 코드를 집합하여 읽기와 유지보수를 편리하게 할 수 있다.
  • 구성 요소 트리의 등급이 얕아진다. 원래의 코드에서 우리는 HOC/renderprops 등 방식으로 구성 요소의 상태를 복용하고 기능을 강화하는 등 구성 요소 트리의 층수와 렌더링을 증가시켰다. 그러나 React Hooks에서 이러한 기능은 강력한 자체 정의된 Hooks를 통해 실현할 수 있다.
  • 이 방면에 관한 글은 사용 장면에 따라 각각 예를 들어 설명하여 이해를 돕고 React Hooks의 대부분 특성을 능숙하게 활용할 수 있도록 도와줍니다.
    블로그github 주소: https://github.com/fengshi123/blog

    1. State Hook


    1. 기초용법

    
    function State(){
      const [count, setCount] = useState(0);
      return (
          <div>
              <p>You clicked {count} times</p>
              <button onClick={() => setCount(count + 1)}>
                  Click me
              </button>
          </div>
      )
    }
    

    2. 업데이트


    업데이트는 다음과 같은 두 가지 방식으로 나뉘는데 그것이 바로 업데이트와 함수식 업데이트이다. 그 응용 장면의 구분점은 다음과 같다.
  • 구state에 의존하지 않는 값을 직접 업데이트한다.
  • 함수식 업데이트는 구state에 의존하는 값이다.
  • 
    //  
    setState(newCount);
    
    //  
    setState(prevCount => prevCount - 1);
    
    

    3. 합병 실현


    class 구성 요소의 setState 방법과 달리useState는 자동으로 업데이트 대상을 통합하지 않고 직접 교체합니다.우리는 함수식 setState와 전개 연산자를 결합하여 업데이트 대상을 합병하는 효과를 얻을 수 있다.
    
    setState(prevState => {
      //   Object.assign
      return {...prevState, ...updatedValues};
    });
    

    4. 타성 초기화state


    initialState 매개 변수는 구성 요소의 초기 렌더링에만 작용하고 후속 렌더링은 무시됩니다.그 응용 장면은 다음과 같다. 초기state를 만드는 것이 매우 비쌀 때, 예를 들어 복잡한 계산을 통해 얻어야 한다.그러면 함수를 불러와서 함수에서 초기state를 계산하고 되돌려줍니다. 이 함수는 초기에 렌더링할 때만 호출됩니다.
    
    const [state, setState] = useState(() => {
      const initialState = someExpensiveComputation(props);
      return initialState;
    });
    

    5. 일부 중점


    (1)class의this 같지 않다.setState, Hook 업데이트state 변수는 항상 통합되지 않고 교체됩니다.
    (2) 단일state 변수가 아니라 여러 개의state 변수를 사용하는 것을 추천합니다. 왜냐하면state의 교체 논리가 합병 논리가 아니라 후속적인 관련state 논리 추출에 유리하기 때문입니다.
    (3) State Hook의 업데이트 함수를 호출하여 현재state에 전송하면 React는 하위 구성 요소의 렌더링과 effect의 실행을 건너뜁니다.(React 사용Object.is 비교 알고리즘으로state를 비교합니다.

    2. Effect Hook


    1. 기초용법

    
    function Effect(){
      const [count, setCount] = useState(0);
      useEffect(() => {
        console.log(`You clicked ${count} times`);
      });
    
      return (
          <div>
              <p>You clicked {count} times</p>
              <button onClick={() => setCount(count + 1)}>
                  Click me
              </button>
          </div>
      )
    }
    
    

    2. 지우기 작업


    메모리 유출을 방지하기 위해 제거 함수는 구성 요소가 마운트되기 전에 실행됩니다.구성 요소가 여러 번 렌더링되면, 다음 ffect를 실행하기 전에, 이전 ffect는 제거됩니다. 즉, 이전 ffect에서return 함수를 먼저 실행하고, 이 ffect에서 비return 함수를 실행합니다.
    
    useEffect(() => {
      const subscription = props.source.subscribe();
      return () => {
        //  
        subscription.unsubscribe();
      };
    });
    

    3. 실행 시기


    componentDidMount나componentDidUpdate와 달리useEffect 스케줄링을 사용하는 effect는 브라우저의 화면 업데이트를 막지 않기 때문에 응용 프로그램이 더 빨리 응답하는 것처럼 보입니다.(componentDidMount 또는 componentDidUpdate가 브라우저 업데이트 화면을 막습니다.)

    4. 성능 최적화


    기본적으로 React는 브라우저가 화면 렌더링을 마친 후에 effect 호출을 지연할 때마다 기다립니다.그러나 만약에 특정한 값이 두 번의 렌더링 사이에 변화가 없다면,react는 ffect에 대한 호출을 건너뛰고 수조를useEffect의 두 번째 선택할 수 있는 매개 변수로 전달하면 된다. 아래와 같이count값이 두 번의 렌더링 사이에 변화가 없으면 두 번째 렌더링 후에 ffect의 호출을 건너뛴다.
    
    useEffect(() => {
      document.title = `You clicked ${count} times`;
    }, [count]); //   count  
    

    5, 아날로그 componentDidMount


    한 번만 실행하려면effect(구성 요소 마운트 및 마운트 해제 시에만 실행), 두 번째 매개 변수로 빈 그룹 ([]) 을 전달할 수 있습니다. 아래와 같이 원리는 네 번째 성능 최적화와 같습니다.
    
    useEffect(() => {
      .....
    }, []);
    

    6. 모범 사례


    ffect 외부의 함수가 어떤props와state를 사용했는지 기억하는 것은 매우 어렵다. 이것은 ffect 내부에서 필요한 함수를 설명하고자 하는 이유이기도 하다.
    
    // bad, 
    function Example({ someProp }) {
      function doSomething() {
        console.log(someProp);
      }
    
      useEffect(() => {
        doSomething();
      }, []); // 🔴  (  `doSomething`   `someProp`)
    }
    
    // good, 
    function Example({ someProp }) {
      useEffect(() => {
        function doSomething() {
          console.log(someProp);
        }
    
        doSomething();
      }, [someProp]); // ✅  (  effect   `someProp`)
    }
    
    
    만약 어떤 이유로 함수를 effect 내부로 이동할 수 없다면, 다른 방법이 있습니다.
  • 그 함수를 구성 요소 밖으로 이동할 수 있습니다.그러면 이 함수는 어떤props나state에도 의존하지 않을 뿐만 아니라 의존 목록에도 나타나지 않을 것이다.
  • 만부득이한 경우, 함수를 effect의 의존에 넣을 수 있지만, 그 정의를 usecallback Hook에 감싸 넣을 수 있다.이것은 자신의 의존이 바뀌지 않는 한 렌더링에 따라 바뀌지 않도록 확보한다.
  • eslint-plugin-react-hooks  중의  exhaustive-deps  규칙을 사용하는 것을 추천합니다. 이 규칙은 오류 의존을 추가할 때 경고를 보내고 수정 건의를 합니다.
    
    // 1、 
    npm i eslint-plugin-react-hooks --save-dev
    
    // 2、eslint  
    {
      "plugins": [
        // ...
        "react-hooks"
      ],
      "rules": {
        // ...
        "react-hooks/rules-of-hooks": "error",
        "react-hooks/exhaustive-deps": "warn"
      }
    }
    
    

    7. 몇 가지 중점


    (1)useEffect Hook을componentDidMount,componentDidUpdate와componentWillUnmount 이 세 함수의 조합으로 볼 수 있다.
    (2)React의class 구성 요소에서render 함수는 어떠한 부작용도 있어서는 안 된다.일반적으로 여기서 조작을 실행하기에는 너무 이르다. 우리는 기본적으로 React가 DOM을 업데이트한 후에야 우리의 조작을 실행하기를 바란다.

    3. useContext


    다중 계층 전송 데이터를 처리하는 방식으로 이전 구성 요소 트리에서 계층 조상 구성 요소가 손자 구성 요소에 데이터를 전달하려고 할 때 층층props가 아래로 투과되는 것을 제외하고 우리는 React Context API를 사용하여 이 일을 도와줄 수 있다.사용 예는 아래와 같다
    (1) React Context API를 사용하여 구성 요소 외부에 Context 만들기
    
    import React from 'react';
    const ThemeContext = React.createContext(0);
    export default ThemeContext;
    
    (2) Context를 사용합니다.Provider는 구성 요소를 공유할 수 있는 Context 대상을 제공합니다
    
    import React, { useState } from 'react';
    import ThemeContext from './ThemeContext';
    import ContextComponent1 from './ContextComponent1';
    
    function ContextPage () {
      const [count, setCount] = useState(1);
      return (
        <div className="App">
          <ThemeContext.Provider value={count}>
            <ContextComponent1 />
          </ThemeContext.Provider>
          <button onClick={() => setCount(count + 1)}>
                  Click me
          </button>
        </div>
      );
    }
    
    export default ContextPage;
    
    
    (3) useContext () 갈고리 함수는 Context 대상을 도입하고 그 값을 가져오는 데 사용된다
    
    //  , 
    import React from 'react';
    import ContextComponent2 from './ContextComponent2';
    function ContextComponent () {
      return (
        <ContextComponent2 />
      );
    }
    export default ContextComponent;
    
    
    //  ,  Context  
    import React, { useContext } from 'react';
    import ThemeContext from './ThemeContext';
    function ContextComponent () {
      const value = useContext(ThemeContext);
      return (
        <div>useContext:{value}</div>
      );
    }
    export default ContextComponent;
    
    

    4. useReducer


    1. 기초용법


    useState보다 더 적합한 장면: 예를 들어state 논리 처리가 복잡하고 여러 개의 하위 값을 포함하거나 다음state는 이전의state에 의존한다.예는 아래와 같다.
    
    import React, { useReducer } from 'react';
    interface stateType {
      count: number
    }
    interface actionType {
      type: string
    }
    const initialState = { count: 0 };
    const reducer = (state:stateType, action:actionType) => {
      switch (action.type) {
        case 'increment':
          return { count: state.count + 1 };
        case 'decrement':
          return { count: state.count - 1 };
        default:
          throw new Error();
      }
    };
    const UseReducer = () => {
      const [state, dispatch] = useReducer(reducer, initialState);
    
      return (
        <div className="App">
          <div>useReducer Count:{state.count}</div>
          <button onClick={() => { dispatch({ type: 'decrement' }); }}>useReducer  </button>
          <button onClick={() => { dispatch({ type: 'increment' }); }}>useReducer  </button>
        </div>
      );
    };
    
    export default UseReducer;
    
    

    2. 타성 초기화state

    
    interface stateType {
      count: number
    }
    interface actionType {
      type: string,
      paylod?: number
    }
    const initCount =0 
    const init = (initCount:number)=>{
      return {count:initCount}
    }
    const reducer = (state:stateType, action:actionType)=>{
      switch(action.type){
        case 'increment':
          return {count: state.count + 1}
        case 'decrement':
          return {count: state.count - 1}
        case 'reset':
          return init(action.paylod || 0)
        default:
          throw new Error();
      }
    }
    const UseReducer = () => {
      const [state, dispatch] = useReducer(reducer,initCount,init)
    
      return (
        <div className="App">
          <div>useReducer Count:{state.count}</div>
          <button onClick={()=>{dispatch({type:'decrement'})}}>useReducer  </button>
          <button onClick={()=>{dispatch({type:'increment'})}}>useReducer  </button>
          <button onClick={()=>{dispatch({type:'reset',paylod:10 })}}>useReducer  </button>
        </div>
      );
    }
    export default UseReducer;
    
    

    5. Memo


    아래와 같이 부모 구성 요소가 다시 렌더링될 때, 하위 구성 요소의props와state가 바뀌지 않아도 하위 구성 요소는 다시 렌더링됩니다.
    import React, { memo, useState } from 'react';
    
    //  
    const ChildComp = () => {
      console.log('ChildComp...');
      return (<div>ChildComp...</div>);
    };
    
    //  
    const Parent = () => {
      const [count, setCount] = useState(0);
    
      return (
        <div className="App">
          <div>hello world {count}</div>
          <div onClick={() => { setCount(count => count + 1); }}> </div>
          <ChildComp/>
        </div>
      );
    };
    
    export default Parent;
    
    
    개선: 우리는 memo를 사용하여 한 층을 싸면 위의 문제를 해결할 수 있다.그러나 부모 구성 요소가 하위 구성 요소에 전달되지 않은 상황과 부모 구성 요소가 간단한 유형의 매개 변수를 하위 구성 요소에 전달하지 않은 상황(예를 들어string,number,boolean 등)만 해결한다.복잡한 속성이 있으면 useCallback(리셋 이벤트)이나 usememo(복잡한 속성)를 사용해야 합니다
    
    //  
    const ChildComp = () => {
      console.log('ChildComp...');
      return (<div>ChildComp...</div>);
    };
    
    const MemoChildComp = memo(ChildComp);
    
    

    6. useMemo


    다음 장면을 가정하면 부모 구성 요소가 하위 구성 요소를 호출할 때 info 대상 속성을 전달하고 부모 구성 요소 단추를 누르면 컨트롤러가 하위 구성 요소가 렌더링된 정보를 출력하는 것을 발견할 수 있습니다.
    
    import React, { memo, useState } from 'react';
    
    //  
    const ChildComp = (info:{info:{name: string, age: number}}) => {
      console.log('ChildComp...');
      return (<div>ChildComp...</div>);
    };
    
    const MemoChildComp = memo(ChildComp);
    
    //  
    const Parent = () => {
      const [count, setCount] = useState(0);
      const [name] = useState('jack');
      const [age] = useState(11);
      const info = { name, age };
    
      return (
        <div className="App">
          <div>hello world {count}</div>
          <div onClick={() => { setCount(count => count + 1); }}> </div>
          <MemoChildComp info={info}/>
        </div>
      );
    };
    
    export default Parent;
    
    
    분석 원인:
  • 부모 구성 요소 단추를 누르면 부모 구성 요소의 렌더링을 터치합니다.
  • 부모 구성 요소 렌더링, const info = {name,age} 줄은 새 대상을 다시 생성하여 하위 구성 요소에 전달되는 info 속성 값의 변화를 초래하고 하위 구성 요소를 다시 렌더링합니다.
  • 해결:
    usememo를 사용하여 객체 속성을 한 레이어에 패키지화합니다. usemo에는 두 가지 매개변수가 있습니다.
  • 첫 번째 매개 변수는 함수입니다. 되돌아오는 대상은 같은 인용을 가리키며 새로운 대상을 만들지 않습니다.
  • 두 번째 파라미터는 수조이다. 수조의 변수가 바뀔 때만 첫 번째 파라미터의 함수가 새로운 대상을 되돌려준다.
  • 
    import React, { memo, useMemo, useState } from 'react';
    
    //  
    const ChildComp = (info:{info:{name: string, age: number}}) => {
      console.log('ChildComp...');
      return (<div>ChildComp...</div>);
    };
    
    const MemoChildComp = memo(ChildComp);
    
    //  
    const Parent = () => {
      const [count, setCount] = useState(0);
      const [name] = useState('jack');
      const [age] = useState(11);
      
      //   useMemo  
      const info = useMemo(() => ({ name, age }), [name, age]);
    
      return (
        <div className="App">
          <div>hello world {count}</div>
          <div onClick={() => { setCount(count => count + 1); }}> </div>
          <MemoChildComp info={info}/>
        </div>
      );
    };
    
    export default Parent;
    
    

    7. useCallback


    이어서 6장의 예에서 이벤트를 하위 구성 요소에 전달해야 한다고 가정하면 다음과 같다. 부모 구성 요소 단추를 누르면 컨트롤러가 하위 구성 요소가 렌더링된 정보를 출력하고 하위 구성 요소가 다시 렌더링되었다는 것을 알 수 있다.
    
    import React, { memo, useMemo, useState } from 'react';
    
    //  
    const ChildComp = (props:any) => {
      console.log('ChildComp...');
      return (<div>ChildComp...</div>);
    };
    
    const MemoChildComp = memo(ChildComp);
    
    //  
    const Parent = () => {
      const [count, setCount] = useState(0);
      const [name] = useState('jack');
      const [age] = useState(11);
      const info = useMemo(() => ({ name, age }), [name, age]);
      const changeName = () => {
        console.log(' ...');
      };
    
      return (
        <div className="App">
          <div>hello world {count}</div>
          <div onClick={() => { setCount(count => count + 1); }}> </div>
          <MemoChildComp info={info} changeName={changeName}/>
        </div>
      );
    };
    
    export default Parent;
    
    
    원인 분석:
  • 부모 구성 요소 단추를 누르면 부모 구성 요소의count 변수 값 (부모 구성 요소의state 값) 을 바꾸어 부모 구성 요소를 다시 렌더링합니다.
  • 부모 구성 요소가 다시 렌더링될 때changeName 함수를 다시 만듭니다. 즉, 하위 구성 요소에 전달된changeName 속성이 바뀌어 하위 구성 요소가 렌더링됩니다.
  • 해결:
    부모 구성 요소의 changeName 방법을 수정합니다. useCallback 갈고리 함수로 감싸십시오. useCallback 매개 변수는usemo와 유사합니다.
    
    import React, { memo, useCallback, useMemo, useState } from 'react';
    
    //  
    const ChildComp = (props:any) => {
      console.log('ChildComp...');
      return (<div>ChildComp...</div>);
    };
    
    const MemoChildComp = memo(ChildComp);
    
    //  
    const Parent = () => {
      const [count, setCount] = useState(0);
      const [name] = useState('jack');
      const [age] = useState(11);
      const info = useMemo(() => ({ name, age }), [name, age]);
      const changeName = useCallback(() => {
        console.log(' ...');
      }, []);
    
      return (
        <div className="App">
          <div>hello world {count}</div>
          <div onClick={() => { setCount(count => count + 1); }}> </div>
          <MemoChildComp info={info} changeName={changeName}/>
        </div>
      );
    };
    
    export default Parent;
    
    

    8. useRef


    다음은 useRef의 두 가지 사용 장면에 대해 설명합니다.

    1,dom 요소 가리키기


    아래와 같이 useRef로 만든 변수는 input 요소를 가리키고 페이지를 렌더링한 후 input에 초점을 맞춘다
    
    import React, { useRef, useEffect } from 'react';
    const Page1 = () => {
      const myRef = useRef<HTMLInputElement>(null);
      useEffect(() => {
        myRef?.current?.focus();
      });
      return (
        <div>
          <span>UseRef:</span>
          <input ref={myRef} type="text"/>
        </div>
      );
    };
    
    export default Page1;
    
    

    2. 저장 변수


    useref는reacthook에서 작용합니다. 홈페이지에서 말한 바와 같이 변수와 같고,this와 유사합니다. 이것은 상자와 같습니다. 당신은 모든 것을 저장할 수 있습니다.createRef는 렌더링할 때마다 새로운 인용을 되돌려주고,useRef는 매번 같은 인용을 되돌려줍니다. 아래와 같습니다.
    
    import React, { useRef, useEffect, useState } from 'react';
    const Page1 = () => {
        const myRef2 = useRef(0);
        const [count, setCount] = useState(0)
        useEffect(()=>{
          myRef2.current = count;
        });
        function handleClick(){
          setTimeout(()=>{
            console.log(count); // 3
            console.log(myRef2.current); // 6
          },3000)
        }
        return (
        <div>
          <div onClick={()=> setCount(count+1)}> count</div>
          <div onClick={()=> handleClick()}> </div>
        </div>
        );
    }
    
    export default Page1;
    
    

    9. useImperativeHandle


    장면 사용: ref를 통해 얻을 수 있는 것은 전체dom 노드입니다. useImperativeHandle을 통해 일부분의 방법과 속성만 노출되는 것을 제어할 수 있습니다. 전체dom 노드가 아니라.

    10. useLayoutEffect


    그 함수 서명은useEffect와 같지만, 모든 DOM이 변경된 후에 effect를 동기화합니다. 여기서 더 이상 예를 들지 않습니다.
    uselayout Effect와 평소에 쓰는 클래스 구성 요소의componentDidMount와componentDidUpdate를 동시에 실행합니다.
    useEffect는 이번 업데이트가 완료된 후, 즉 첫 번째 방법으로 실행이 완료된 후, 다시 한 번 작업 스케줄링을 열고, 다음 작업 스케줄링에서useEffect를 실행합니다.

    총결산


    이 방면의 글에 대해 우리는 사용 장면에 따라 각각 예를 들어 설명하는데 당신이 이해하고 이해하는 데 도움이 되고 React Hooks의 대부분 특성을 능숙하게 활용할 수 있기를 바랍니다.
    이 React Hooks의 상용 장면 사용(소결)에 관한 글은 여기까지 소개합니다. 더 많은 React Hooks의 상용 장면 내용은 저희 이전의 글을 검색하거나 아래의 관련 글을 계속 훑어보십시오. 앞으로 많은 응원 부탁드립니다!

    좋은 웹페이지 즐겨찾기