React.useImperativeHandle의 구체적인 예

11794 단어 reacthooksjavascript
React 후크 도구 상자에서 상대적으로 모호한 도구는 useImperativeHandle 입니다. 꽤 오랫동안 주변에 있었음에도 불구하고.

대부분의 경우 필요하지 않으며 문서에서도 더 선언적인 솔루션을 선택하여 사용을 권장하지 않습니다.









댄 아브라모프










당신이 초보자라면 그 중 절반이 필요하지 않습니다. (ImperativeHandle을 사용하려는 이유는 무엇입니까?) 기본 항목에 충실하십시오. 🙂 나중에 다른 것이 필요하다는 것을 알게 되면 그들은 당신을 위해 거기에 있습니다.


오후 13:18 - 2019년 1월 27일









때로는 유용할 수 있습니다. 이 게시물에서는 우리@Cloudinary가 최근에 발견한 한 가지 용도를 보여주고자 합니다.

자세히 보기



먼저 후크의 구현을 자세히 살펴보겠습니다.
다른 후크와 마찬가지로 실제 구현은 반응이 아닌 react-dom 패키지의 일부로 게시됩니다.


function imperativeHandleEffect(create, ref) {
  if (typeof ref === 'function') {
    ref(create());
  } else if (ref !== null && ref !== undefined) {
    ref.current = create();
  }
}


위의 코드는 매우 간단합니다. 실제 코드는 here 입니다.

이 함수는 mountEffect()에 의해 래핑되며 이는 useEffect와 동일하게 실행됨을 의미합니다.

보시다시피 useImperativeHandle은 create 함수를 실행하고 이를 ref 매개변수에 할당합니다. 함수이면 입력으로 전달되고 그렇지 않으면 .current 값이 됩니다.

useImperativeHandle accepts a dependency list, as with other hooks. However, React will actually add the ref to the list of dependencies for you but eslint-plugin-react-hooks doesnt seem to know that.



도전



그렇다면 ReactJS가 제공하는 간단한 예제 외에 무엇을 할 수 있을까요?
우리의 경우에는 디자인 시스템의 일부로 자체 UI 구성 요소를 구축하고 있습니다.

새 구성 요소인 NumberField로 래핑한 TextField 구성 요소가 있습니다. 대부분의 경우 NumberField는 Text 대응 항목과 매우 유사합니다. 그러나 우리는 위/아래 버튼에 대해 일관된 동작과 룩앤필을 원했습니다.



그러나 이들은 브라우저 간에 다르게 보이므로 자체 UI가 필요했습니다.

그런 다음 도전적인 부분이 왔습니다. controlled component 에 강제로 넣지 않고 React-land의 입력 값을 어떻게 제어합니까? 구성 요소의 사용은 제어 여부를 결정해야 합니다. 따라서 구성 요소 자체는 안됩니다.

제 동료가 매우 유용한 HTMLInputElement.stepUp()HTMLInputElement.stepDown() 방법을 알려 주었습니다. 이는 value 를 전달하지 않고 입력 값을 변경할 수 있음을 의미했습니다.

엄청난!



그러나 NumberField는 TextField를 래핑합니다. 따라서 내부 TextField에 외부 참조를 전달하는 동안 자체 참조를 사용할 수 있어야 합니다.

또 다른 제약 - ref는 함수일 수도 있고 객체일 수도 있습니다(useRef의 결과). 따라서 우리는 둘 다 지원해야 합니다(익숙하게 들리나요?).

여기서 useImperativeHandle이 도움이 됩니다. 그것 없이는 문제를 해결할 수 없는 것은 아닙니다. 솔루션을 매우 간결한 하나의 라이너로 줄였습니다. 우!

암호



먼저 TextInput을 정의합니다. 물론 이 문서의 목적을 위해 단순화되었습니다.

const TextInput = forwardRef(
    ({ type = "text", defaultValue, value, onChange, className }, ref) => {
      return (
        <input className={className} type={type} ref={ref} value={value} defaultValue={defaultValue} onChange={onChange} />
      );
    }
  );



다음으로 기본 위/아래 버튼을 숨길 숫자 입력용 컨테이너를 정의합니다.


const NumberInputWrapper = styled.div`
  display: flex;

  input[type="number"] {
    &::-webkit-outer-spin-button,
    &::-webkit-inner-spin-button {
      -webkit-appearance: none;
      margin: 0;
    }

    appearance: textfield;
  }
`;


마지막으로 NumberInput을 정의합니다.

const NumberInput = forwardRef((props, ref) => {
  const internalRef = useRef(null);

  useImperativeHandle(ref, () => internalRef.current, []);

  const onUp = useCallback(() => {
    internalRef.current.stepUp();
  }, [internalRef]);

  const onDown = useCallback(() => {
    internalRef.current.stepDown();
  }, [internalRef]);

  return (
    <NumberInputWrapper>
      <TextInput {...props} type="number" ref={internalRef} />
      <NumberButtonsContainer>
        <NumberButton onClick={onUp}>⬆️</NumberButton>
        <NumberButton onClick={onDown}>⬇️</NumberButton>
      </NumberButtonsContainer>
    </NumberInputWrapper>
  );
});


물론 위 코드에서 중요한 부분은 useImperativeHandle에 대한 호출입니다.


  useImperativeHandle(ref, () => internalRef.current, []);


첫 번째 인수는 외부에서 받은 ref입니다. create 함수 내에서 내부 참조의 결과를 반환합니다. 이렇게 하면 외부 코드에서 이전과 같이 ref를 사용할 수 있습니다. 내부적으로는 internalRef 인스턴스를 사용하여 DOM을 통해 입력을 변경할 수 있습니다.

단순한!

추신 전체 코드 예제는 여기codesandbox에서 찾을 수 있습니다.

좋은 웹페이지 즐겨찾기