React에서 의존성 주입을 수행해야 하는 시기 및 이유

우리의 React 응용 프로그램은 많은 작은 구성 요소나 모듈로 구성되어 있습니다.우리가 작성한 구성 요소는 때때로 서로 의존할 수 있다.응용 프로그램의 규모가 증가함에 따라 구성 요소 간의 의존 관계를 적절하게 관리할 필요가 있다.주입에 의존하는 것은 이 문제를 해결하는 일종의 유행 모델이다.
본문에서 우리는 토론할 것이다
  • 의존 주입 모드를 언제 적용해야 하는가
  • 고급 어셈블리 의존 주입(HOC)
  • React 상하문이 있는 의존항 주입
  • Note: If you have no prior knowledge of dependency injection, I would recommend the following blog post


    아래의 예를 고려해 봅시다.
    // app.js
    function App() {
      const [webSocketService, setwebSocketServicet] = React.useState({});
      React.useEffect(() => {
        // initialize service
        setwebSocketServicet({
          user: `some user`,
          apiKey: `some string`,
          doStuff: () => console.log("doing some function")
        });
      }, []);
      return (
        <div>
          <B socket={webSocketService} />
        </div>
      );
    }
    
    서비스를 초기화하고 인용을 도구로 전달하는 App 구성 요소가 있습니다.
    // B.js
    function B(props) {
      return (
        <div>
          <A {...props} />
        </div>
      );
    }
    
    // A.js
    function A(props) {
      // Do something with web socket
      const doWebSocket = () => {
        props.socket.doStuff();
      };
      return (
        <div>
          <button onClick={() => doWebSocket()}>Click me</button>
          {props.children}
        </div>
      );
    }
    
    조립품BApp로부터 아이템을 받아 A에 전달한다.B 통과하는 아이템에는 아무런 효과가 없다.우리의 websocket 실례는 그것을 사용하는 A 구성 요소에 어떤 방식으로 도착해야 한다.이것은 매우 기본적인 예시 응용 프로그램이지만, 현실 세계에서, 우리가 많은 구성 요소가 서로 내부에 끼워 넣을 때, 우리는 반드시 이 속성을 계속 전달해야 한다.예컨대
    <ExampleComponentA someProp={someProp}>
      <X someProp={someProp}>
        <Y someProp={someProp}>
          //.... more nesting 
          //... finally Z will use that prop
          <Z someProp={someProp} /> 
        </Y>
      </X>
    </ExampleComponentA>
    
    
    이 구성 요소 중 많은 것들이 이 도구를 아이에게 전달하는 대리인으로 충당되었다.이것도 코드의 테스트 가능성을 낮춘다. 왜냐하면 우리가 이 구성 요소 (X 또는 Y) 를 위해 테스트를 작성할 때, 이 속성의 유일한 목적이 하위 트리에 전달하는 것일지라도, 우리는 반드시 시뮬레이션 someProp 을 해야 하기 때문이다.
    이제 고급 구성 요소의 의존항 주입을 사용하여 이 문제를 해결하는 방법을 보여 줍니다.deps.js라는 파일을 만듭니다. 이 파일에 두 가지 함수가 있습니다.
    import React from "react";
    
    let dependencies = {};
    
    export function register(key, dependency) {
      dependencies[key] = dependency;
    }
    
    export function fetch(key) {
      if (dependencies[key]) return dependencies[key];
      console.log(`"${key} is not registered as dependency.`);
    }
    
    dependencies 객체에는 모든 종속성 이름과 값이 저장됩니다.register 함수는 의존항만 등록하고 fetch 함수는 주어진 키의 의존항을 가져옵니다.
    이제 주입 속성을 포함하는 조합 어셈블리를 반환하는 HOC를 생성합니다.
    export function wire(Component, deps, mapper) {
      return class Injector extends React.Component {
        constructor(props) {
          super(props);
          this._resolvedDependencies = mapper(...deps.map(fetch));
        }
        render() {
          return (
            <Component
              {...this.state}
              {...this.props}
              {...this._resolvedDependencies}
            />
          );
        }
      };
    }
    
    우리의 wire 함수에서 우리는 Component, 하나의 dependencies 수조와 하나의 mapper 대상을 전달하고 그 중 의존항을 지탱하는 새로운 Injected 구성 요소로 되돌아간다.우리는 의존항을 찾고 있으며, 구조 함수에 그것들을 비추고 있다.우리도 lifecycle 갈고리에서 이 점을 실현할 수 있지만, 간단하게 보기 위해 현재 구조 함수를 계속 사용하자.
    자, 우리 첫 번째 예로 돌아갑시다.App 구성 요소를 다음과 같이 변경합니다.
    + import { register } from "./dep";
    
    function App() {
      const [webSocketService, setwebSocketServicet] = React.useState(null);
      React.useEffect(() => {
        setwebSocketServicet({
          user: `some user`,
          apiKey: `some string`,
          doStuff: () => console.log("doing some function")
        });
      }, [webSocketService]);
    + if(webSocketService) {
    +   register("socket", webSocketService);
    +   return <B />;
    + } else {
    +   return <div>Loading...</div>;
    + }
    }
    
    WebSocket 서비스를 초기화하고 register 함수로 등록했습니다.현재 우리 A 구성 요소에서, 우리는 그것을 연결하기 위해 다음과 같은 변경 사항을 했다.
    +const GenericA = props => {
    +  return (
    +    <button onClick={() => console.log("---->>", +props.socket.doStuff())}>
    +      Push me
    +    </button>
    +  );
    +};
    +const A = wire(GenericA, ["socket"], socket => ({ socket }));
    
    이렇게지금 우리는 대리 전달을 걱정할 필요가 없다.이렇게 하면 또 다른 좋은 점이 있다.JavaScript의 일반적인 모듈 시스템에는 캐시 메커니즘이 있습니다.

    Modules are cached after the first time they are loaded. This means (among other things) that every call to require('foo') will get exactly the same object returned, if it would resolve to the same file.

    Multiple calls to require('foo') may not cause the module code to be executed multiple times. This is an important feature. With it, "partially done" objects can be returned, thus allowing transitive dependencies to be loaded even when they would cause cycles.


    *** 노드에서 가져옵니다.js 문서
    이것은 우리가 의존항을 초기화할 수 있다는 것을 의미한다. 의존항은 캐시되고, 우리는 그것을 다시 불러올 필요가 없이 여러 곳에서 주입할 수 있다.이 모듈을 내보낼 때, 우리는 하나의 예를 만들고 있습니다.
    하지만 지금은 2019년, 우리는 상하문api를 사용하고 싶다, 그렇지?좋습니다. React 상하문을 어떻게 사용해서 의존 주입을 하는지 봅시다.

    Note: If you would like to know more about how other SOLID principles apply to React. Check out my previous post

    context.js라는 파일을 만듭니다.
    
    import { createContext } from "react";
    
    const Context = createContext({});
    
    export const Provider = Context.Provider;
    export const Consumer = Context.Consumer;
    
    
    현재, 우리 App 구성 요소에서, 우리는register 함수를 사용하지 않고 상하문 제공 프로그램을 사용할 수 있습니다.그래서 좀 바꿔달래요.
    +import { Provider } from './context';
    
    function App() {
      const [webSocketService, setwebSocketServicet] = React.useState(null);
      React.useEffect(() => {
        setwebSocketServicet({
          user: `some user`,
          apiKey: `some string`,
          doStuff: () => console.log("doing some function")
        });
      }, []);
    
      if (webSocketService) {
    +    const context = { socket: webSocketService };
        return (
    +      <Provider value={ context }>
            <B />
    +      </Provider>
        )
      } else {
        return <div>Loading...</div>;
      }
    }
    
    현재, 우리 A 구성 요소에서, 우리는 HOC를 연결하는 것이 아니라 상하문 소비자를 사용합니다.
    function A(props) {
      return (
        <Consumer>
          {({ socket }) => (
            <button onClick={() => console.log(socket.doStuff())}>Click me</button>
          )}
        </Consumer>
      );
    }
    
    이것이 바로 우리가 React 상하문을 사용하여 의존 주입을 하는 방식이다.

    마지막 생각


    많은 React 라이브러리에서 종속성 주입을 사용합니다.React Router 및 Redux는 주목할 만한 제품입니다.DI는 JavaScript 세계의 당면 과제입니다.이러한 기술을 이해하면 더 나은 자바스크립트 개발자가 될 수 있을 뿐만 아니라 대형 응용 프로그램을 구축할 때 비판적으로 자신의 선택을 생각하게 할 수 있다.나는 네가 이 문장을 좋아하길 바란다.좋아하는 것 좀 남겨 주세요.
    다음까지.
    ***주의: 이 글은 현재 진행 중인 작업으로 내용을 계속 업데이트하고 있습니다.따라서 피드백을 보내주시면 감사하겠습니다***

    좋은 웹페이지 즐겨찾기