react hooks 구현 원리

18195 단어

react hooks 구현


Hooks가 어떤 문제를 해결했는지.

React의 디자인 철학에서 간단하게 말하면 다음과 같은 공식으로 나타낼 수 있다.
UI = f(data)

등호의 왼쪽에 UI가 대표하는 최종적으로 그려진 인터페이스;등호의 오른쪽은 함수, 즉 우리가 쓴 React와 관련된 코드이다.데이터는 데이터입니다. React에서 데이터는state나props일 수 있습니다.UI는 데이터를 매개 변수로 f에 전달하여 연산한 결과입니다.이 공식의 의미는 인터페이스를 렌더링하려면 DOM 요소를 직접 조종하지 말고 데이터를 수정하고 데이터가React를 구동해서 인터페이스를 수정하는 것이다.우리 개발자가 해야 할 일은 합리적인 데이터 모델을 설계하여 우리의 코드가 데이터에 근거하여 인터페이스를 어떻게 그려야 하는지를 설명하고 브라우저의DOM 트리 구조를 어떻게 조작해야 하는지를 고민하지 않도록 하는 것이다.전체적인 설계 원칙:
  • 완전한 데이터 기반 인터페이스
  • 모든 구성 요소
  • props를 사용하여 구성 요소 간 통신
  • 이와 함께 가져온 문제는 어떤 것들이 있습니까?
  • 구성 요소 간의 데이터 교류 결합도가 너무 높고 많은 구성 요소 간에 공유해야 하는 데이터는 층층이 전달되어야 한다.전통적인 해결 방법은요!
  • 변수 증가
  • 고급 함수 투전
  • 제3자 데이터 관리 라이브러리 도입,redux,mobx
  • 상기 세 가지 디자인 방식은 모두 데이터를 부 노드나 최고 노드로 끌어올린 다음에 데이터가 층층이 전달된다
  • ClassComponet 생명주기의 학습 비용과 강력한 관련 코드 논리는 생명주기 갈고리 함수의 집행 과정 때문에 코드를 강제로 분리해야 한다.일반:
  • class SomeCompoent extends Component {
    	
      componetDidMount() {
        const node = this.refs['myRef'];
        node.addEventListener('mouseDown', handlerMouseDown);
        node.addEventListener('mouseUp', handlerMouseUp)
      }
      
      ...
      
      componetWillunmount() {
        const node = this.refs['myRef'];
        node.removeEventListener('mouseDown', handlerMouseDown)
        node.removeEventListener('mouseUp', handlerMouseUp)
      }
    }
    

    후크스의 등장은 위의 문제들이 모두 쉽게 풀린다고 할 수 있다.'리액트 훅스로 바퀴 만들기'도입

    Hooks API 유형


    공식 성명에 따르면 hooks는 완전히 뒤로 호환되며,class componet은 제거되지 않으며, 개발자로서 최신 API로 천천히 이동할 수 있다.
    Hooks는 주로 세 가지로 나뉜다.
  • State hooks: function componet에서 state
  • 를 사용할 수 있음
  • Effect hooks: function componet에서 라이프 사이클과 side effect
  • 를 사용할 수 있습니다.
  • Custom hooks:react에서 제공하는use State,use Reducer,use Effect,use Ref 등에 따라 자신이 필요로 하는 hooks
  • 를 사용자 정의한다.
    다음은 Hooks에 대해 알아보겠습니다.

    State hooks를 먼저 접했습니다.


    use State는 우리가 처음으로React Hooks를 접한 것이다. 그 주요 역할은Function Component가state를 사용할 수 있도록 하고 매개 변수를 받아들여state의 초기 값으로 삼아 현재state와dispatch를 되돌려주는 것이다.
    import { useState } from 'react';
    
    function Example() {
      // Declare a new state variable, which we'll call "count"
      const [count, setCount] = useState(0);
      return (
        <div>
          <p>You clicked {count} timesp>
          <button onClick={() => setCount(count + 1)}>
            Click me
          button>
        div>
      );
    }
    

    그 중에서useState는 여러 번 성명할 수 있다.
    function FunctionalComponent () {
      const [state1, setState1] = useState(1)
      const [state2, setState2] = useState(2)
      const [state3, setState3] = useState(3)
      
      return <div>{state1}{...}div>
    }
    

    이에 대응하는 hooks와useReducer가 있습니다. 상태가 다른 종류의 업데이트 처리에 대응하는 경우useReducer를 사용할 수 있습니다.

    그다음에 Effect hooks를 접했어요.


    useEffect의 사용은 Function Componet 구성 요소가life-cycles 성명 주기 함수를 갖추도록 하는 것이다.예를 들어componetDidMount,componetDidUpdate,shouldCompoentUpdate와componetWiillunmount는 모두 이 함수에 집중되어 집행되고useEffect라고 한다.이 함수는 Redux의subscribe와 약간 유사하며,props,state가render를 터치할 때마다 실행됩니다.(구성 요소의 첫 번째render와 매번 업데이트 후에 터치합니다.)왜 useEffect라고 하죠?공식적인 설명: 왜냐하면 우리는 보통 생명 주기 내에 많은 조작을 하면side-effect(부작용)의 조작이 발생하기 때문이다. 예를 들어DOM,fetch 데이터 업데이트 등이다.
    useEffect는 다음을 사용합니다.
    import React, { useState, useEffect } from 'react';
    
    function useMousemove() {
    	const [client, setClient] = useState({x: 0, y: 0});
      
      useEffect(() => {
       
        const handlerMouseCallback = (e) => {
        	setClient({
          	x: e.clientX,
            y: e.clientY
          })
        };
        //       render   ,    didMount    
      	document.addEventListener('mousemove', handlerMouseCallback, false);
        
        return () => {
          //          
        	document.removeEventListener('mousemove', handlerMouseCallback, false);
        }
      })
      
      return client;
    }
    
    

    그 중에서useEffect는 구성 요소가 처음render 이후didMount에 호출되었을 뿐이고, 구성 요소가 마운트 해제되었을 때 unmount 이후에 호출되었을 뿐입니다. DOM 업데이트 후에 동기화할 필요가 있으면useLayoutEffect를 사용할 수 있습니다.

    마지막으로 접한 건 커스텀 훅스예요.


    정부에서 제공하는useXXXAPI를 자신의 업무 장면과 결합하여 사용자 정의 개발에 필요한custom hooks를 사용하여 업무 개발 데이터를 추출하고 수요에 따라 도입할 수 있다.업무 데이터와 보기 데이터의 충분한 결합을 실현하다.

    Hooks 구현 방법


    위의 기초를 바탕으로 hooks의 사용에 대해 기본적인 이해를 얻었을 것이다. 다음은 hooks 원본 코드와 결합하여 hooks가 무상태 구성 요소를 어떻게 보존할 수 있는지의 원리를 박리한다.
    Hooks 소스는 Reactreact-reconclier**의 ReactFiberHooks입니다.js, 코드는 600줄이 있어서 이해하기에도 편리합니다. 원본 주소: 여기
    Hooks의 기본 유형:
    type Hooks = {
    	memoizedState: any, //          Fiber
      baseState: any, //     initialState,      dispatch    newState
      baseUpdate: Update<any> | null,//         Update ,       ,       update,   react         ,    
      queue: UpdateQueue<any> | null,// UpdateQueue   
      next: Hook | null, // link      hooks,   next      hooks
    }
     
    type Effect = {
      tag: HookEffectTag, // effectTag      hook     life-cycles       
      create: () => mixed, //     callback
      destroy: (() => mixed) | null, //    callback
      deps: Array<mixed> | null,
      next: Effect, //    
    };
    

    React Hooks 전역에는 Hooks API를 호출할 때마다 함수workInProgressHook를 먼저 호출하는 createWorkInProgressHooks 변수가 유지됩니다.
    function createWorkInProgressHook() {
      if (workInProgressHook === null) {
        // This is the first hook in the list
        if (firstWorkInProgressHook === null) {
          currentHook = firstCurrentHook;
          if (currentHook === null) {
            // This is a newly mounted hook
            workInProgressHook = createHook();
          } else {
            // Clone the current hook.
            workInProgressHook = cloneHook(currentHook);
          }
          firstWorkInProgressHook = workInProgressHook;
        } else {
          // There's already a work-in-progress. Reuse it.
          currentHook = firstCurrentHook;
          workInProgressHook = firstWorkInProgressHook;
        }
      } else {
        if (workInProgressHook.next === null) {
          let hook;
          if (currentHook === null) {
            // This is a newly mounted hook
            hook = createHook();
          } else {
            currentHook = currentHook.next;
            if (currentHook === null) {
              // This is a newly mounted hook
              hook = createHook();
            } else {
              // Clone the current hook.
              hook = cloneHook(currentHook);
            }
          }
          // Append to the end of the list
          workInProgressHook = workInProgressHook.next = hook;
        } else {
          // There's already a work-in-progress. Reuse it.
          workInProgressHook = workInProgressHook.next;
          currentHook = currentHook !== null ? currentHook.next : null;
        }
      }
      return workInProgressHook;
    }
    
    

    다음 hooks 코드를 실행해야 한다고 가정하십시오.
    function FunctionComponet() {
    	
      const [ state0, setState0 ] = useState(0);
      const [ state1, setState1 ] = useState(1);
      useEffect(() => {
      	document.addEventListener('mousemove', handlerMouseMove, false);
        ...
        ...
        ...
        return () => {
          ...
          ...
          ...
        	document.removeEventListener('mousemove', handlerMouseMove, false);
        }
      })
      
      const [ satte3, setState3 ] = useState(3);
      return [state0, state1, state3];
    }
    

    React Hooks의 간단한 원리를 이해하면 Hooks의 직렬은 하나의 그룹이 아니지만 하나의 체인식 데이터 구조로 루트 노드workInProgressHook에서 아래로next를 통해 직렬로 연결된다.이것이 바로 Hooks는 끼워서 사용할 수 없고 조건 판단에서 사용할 수 없고 순환에서 사용할 수 없는 이유다.그렇지 않으면 체인 구조를 파괴할 것이다.

    질문 1:useState dispatch 함수를 사용하는 Function Component와 바인딩하는 방법


    코드부터 살펴보겠습니다.
    
    import React, { useState, useEffect } from 'react';
    import ReactDOM from 'react-dom';
    
    const useWindowSize = () => {
    	let [size, setSize] = useState([window.innerWidth, window.innerHeight])
    	useEffect(() => {
    		let handleWindowResize = event => {
    			setSize([window.innerWidth, window.innerHeight])
    		}
    		window.addEventListener('resize', handleWindowResize)
    		return () => window.removeEventListener('resize', handleWindowResize)
    	}, [])
    	return size
    }
    
    
    const App = () => {
    	const [ innerWidth, innerHeight ] = useWindowSize();
      return (
        <ul>
      		<li>innerWidth: {innerWidth}li>
    			<li>innerHeight: {innerHeight}li>
        ul>
      )
    }
    
    ReactDOM.render(<App/>, document.getElementById('root'))
    
    

    State의 역할은 Function Component가 State의 능력을 갖추도록 하는 것이다. 그러나 개발자에게 Function Component에 hooks 함수를 도입하면 디스패치가 현재 구성 요소에 정확하게 작용할 수 있다. 무심코 이런 의문이 생길 수 있다. 이 의문을 가지고 원본 코드를 읽어보자.
    function useState(initialState){
      return useReducer(
        basicStateReducer,
        // useReducer has a special case to support lazy useState initializers
        (initialState: any),
      );
    }
    
    function useReducer(reducer, initialState, initialAction) {
      //        rendering   Fiber
    	let fiber = (currentlyRenderingFiber = resolveCurrentlyRenderingFiber());
      workInProgressHook = createWorkInProgressHook();
      //         
      ...
      ...
      ...
      // dispathAction            Fiber,     dispatchAction  
      const dispatch = dispatchAction.bind(null, currentlyRenderingFiber,queue,)
      return [workInProgressHook.memoizedState, dispatch];
    }
    
    function dispatchAction(fiber, queue, action) {
    	const alternate = fiber.alternate;
      const update: Update = {
        expirationTime,
        action,
        eagerReducer: null,
        eagerState: null,
        next: null,
      };
      ......
      ......
      ......
      scheduleWork(fiber, expirationTime);
    }
    
    

    전재 대상:https://juejin.im/post/5c4c43f5e51d45518c681a69

    좋은 웹페이지 즐겨찾기