적은 것이 더 많습니다. React 코드를 단순화하여 애플리케이션 성능 향상 - 2부

이것은 React 애플리케이션을 단순화하기 위한 다양한 전략을 다루는 시리즈의 두 번째 기사입니다.

React 구성 요소 단순화



기존 구성 요소를 대폭 분해하지 않고 구성 요소를 단순화하기 위해 취할 수 있는 여러 전략이 있습니다. 각 전략은 다른 블로그에서 다룹니다.
  • Separate state from display, this will help your application align with well established MVC rules
  • 서비스 및 사용자 지정 후크로 처리를 연기
  • 과부하 방지useEffectuseState
  • reduxredux-saga가 실제로 필요한지 확인
  • 구성 요소 간에 기능을 결합하기 위해 고차 구성 요소를 생성합니다
  • .
  • 구성 요소에서 도우미 함수로 계산 논리를 이동하고 사용자 지정 후크를 삽입합니다.
  • 가능한 경우 지연 로딩 및 지연 동작을 사용합니다.

  • 서비스 및 사용자 지정 후크로 처리 연기



    React는 특히 주입 가능한 리소스를 통해 프레임워크에 구워진 Angular와 달리 서비스 개념이 부족합니다. React 개발자로서 우리는 이 기능을 프로젝트에 적용해야 합니다. 서비스는 특정 기능을 깔끔하게 캡슐화하고 TypeScript 캐스팅을 통해 교환 가능한 리소스가 되며 프레젠테이션 계층에서 논리를 이동하는 또 다른 단계입니다.

    종종 동일한 구성 요소에서 가져오기 및 표시를 수행할 수 있는 구성 요소를 볼 수 있습니다.

    const myComponent: React.FC = () => {
      const [ todos, setTodos ] = useState<any>([]);
      useEffect(async () => {
        const result = await axios.get("https://jsonplaceholder.typicode.com/todos");
        const todos = res.data.filter(!!todos.completed));
        setTodos(todos);
      });
    
      return (
        <ul>
        { todos.map(item => (
          <li key={item.id}>
            <a href={`https://jsonplaceholder.typicode.com/todos/${item.id}`>{item.title}</a>
          </li>
        ))}
        </ul>
      )
    }
    


    이 구성 요소에는 그다지 문제가 없어 보이는 표면 수준입니다. 그러나 API, 추가 유효성 검사 및 데이터 조작의 오류 처리를 시작해야 하는 경우에는 어떻게 해야 합니까? 우리의 useEffect 후크는 갑자기 과부하가 걸리고 서비스로 연기될 수 있고 해야 하는 동작으로 부풀려집니다.

    useEffect(async () => {
      try {
        const result = await axios.get("https://jsonplaceholder.typicode.com/todos");
        const todos = res.data.filter(!!todos.completed));
        setTodos(todos);
      } catch (e) {
        setLoaded(false);
        setErrorMessage("Could not load todos, please refresh your browser and make sure you're connected to the internet!");
      }
    });
    

    useEffect 후크에 더 많이 추가할수록 구성 요소가 더 복잡해지고 테스트하기가 더 어려워집니다. 지연/비동기 렌더링은 이미 jest 및 효소 처리 업데이트와 같은 도구를 사용하여 테스트를 어렵게 만들고 있지만 쉽지는 않습니다.

    API 처리를 서비스로 이동하여 일관되게 요청하고 오류를 처리하고 useEffect 코드를 사용자 지정 후크로 분리하여 이 코드를 단순화할 수 있습니다.

    type Todo = { id: number, title: string };
    type TodosService = {
      todos: async (completed?: boolean) => Promise<Array<Todo>>,
      todo: async (id: number) => Promise<Todo>
    };
    
    class TodosServiceImpl implements TodosService {
      async todos(completed?: boolean): Promise<Array<Todo>> {
        try {
          const result = await axios.get("https://jsonplaceholder.typicode.com/todos");
          if (completed !== undefined) {
            return res.data.filter(todo => todo.completed === completed));
          }
          return res.data;
        } catch (e) {
          throw "Could not load todos, please refresh your browser and make sure you're connected to the internet!";
        }
      }
    
      async todo(id: number): Promise<Todo> {
        try {
          const result = await axios.get(`https://jsonplaceholder.typicode.com/todos/${id}`);
          return res.data;
        } catch (e) {
          throw `Could not load todo ${id}, please refresh your browser and make sure you're connected to the internet!`;
        }
      }
    }
    


    교환 가능한 서비스가 필요한 경우 TodosService의 계약을 만족하는 한 새로운 서비스를 제공할 수 있습니다.

    const todosService: TodosService = {
      todos: async (completed?: boolean): Promise<Array<Todo>>  => {...}
      todo: async (id: number): Promise<Todo> => {...}
    }
    // test of the implementation
    


    이제 서비스 구현이 완료되었으므로 구성 요소에서 사용할 수 있습니다.

    const todosService: TodosService = new TodosServiceImpl();
    
    const useTodosLoader = (todosService: TodosService) => {
      const [ todos, setTodos ] = useState<Array<Todos>>([]);
      const [ hasError, setHasError ] = useState<boolean>(false);
      const [ loaded, setLoaded ] = useState<boolean>(false);
    
      useEffect(async () => {
        try {
          const list = await todosService.todos();
          setTodos(list);
          setLoaded(true);
        } catch (e) {
          setHasError(true);
        }
      }, []);
    
      return { todos, hasError, loaded };
    }
    
    const myComponent: React.FC<{ todosService: TodosService }> = ({ todosService }) => {
      const { todos, hasError, loaded } = useTodosLoaded(todosService);
    
      return (
        <ul>
        { todos.map(item => (
          <li key={item.id}>
            <a href={`https://jsonplaceholder.typicode.com/todos/${item.id}`>{item.title}</a>
          </li>
        ))}
        </ul>
      )
    }
    


    위 코드의 모든 측면은 테스트 가능합니다. serice가 호출되었는지 확인할 수 있고 API가 호출되었는지도 확인할 수 있습니다. MyComponent 의 응답을 통해 useTodoLoader 의 로딩을 확인할 수 있으며, 동작을 바로 조롱하고 스텁할 수 있습니다. 구성 요소를 단순화하기 위해 코드 양을 최소한 두 배로 늘렸지만 코드의 증가는 기능 코드와 테스트 코드의 단순성에 정비례합니다.

    사용자 정의 후크를 사용하면 특히 useState 후크를 사용하여 상태를 조작할 때 동작을 논리적으로 그룹화할 수 있습니다. 후크의 출력을 구성 요소에서 사용하도록 노출하여 후크가 상태를 변경할 때 업데이트되도록 할 수 있습니다. 이것은 특히 useState를 사용하여 구성 요소 간의 상태를 유지하는 경우 교차 구성 요소를 사용하는 풍부한 기능을 제공합니다.

    const useMyState = () => {
      const [ myState, setMyState ] = useState(true);
      return { myState, setMyState }
    }
    
    const myComponent = () => {
      const { myState } = useMyState();
      ...
    }
    
    const myOtherComponent = () => {
      const { myState, setMyState } = useMyState();
      useEffect(() => {
        setTimeout(() => {setMyState(false)});
      }, []);
      ...
    }
    


    후크를 사용하여 이벤트 스트림을 구독할 수도 있습니다. 이를 통해 연결되지 않은 여러 구성 요소가 상태 변경을 기반으로 하거나 이벤트에 의해 구동되는 동시에 업데이트할 수 있습니다.

    const myEventStream = () => {
      const [ myState, setMyState ] = useState(null);
      useEffect(() => {
        const subscription = observable.subscribe();
        subscription.next(event => setMyState(event.data));
        return () => subscription.unsubscribe();
      })
    
    }
    
    const myComponent = () => {
      const { myState } = useMyState();
      ...
    }
    
    const myOtherComponent = () => {
      const { myState } = useMyState();
      ...
    }
    
    observable.next({data: { foo: "bar"}});
    // Updates myComponent
    // Updates myOtherComponent
    


    오버로딩 방지 useEffectuseState 에 대해 살펴볼 다음 기사를 기대해 주세요.

    좋은 웹페이지 즐겨찾기