React 기본 3

WebStudy / / 2021. 2. 15. 22:39

Context API

context를 이용하면 단계마다 일일이 props를 넘겨주지 않고도 컴포넌트 트리 전체에 데이터를 제공할 수 있습니다. - Reactjs.org

  • context 없이 props를 전달하여 화면 표시

      import React, {createContext, useContext} from 'react';
    
      function Child({text}){
          return <div>Hello? {text}</div>
      }
    
      function Parent({text}){
          return <Child text = {text}/>
      }
    
      function GrandParent({text}){
          return <Parent text = {text} />
      }
    
      function ContextSample(){
          return <GrandParent text = "GOOD"/>
      }
    
      export default ContextSample;
  • context를 이용하여 자식 컴포넌트에서 바로 데이터를 사용

      import React, {createContext, useContext, useState} from 'react';
    
      const MyContext = createContext('defaultValue');
    
      function Child(){
          const text = useContext(MyContext);
          return <div>Hello? {text}</div>
      }
    
      function Parent(){
          return <Child/>
      }
    
      function GrandParent(){
          return <Parent/>
      }
    
      function ContextSample(){
          const [value, setValue] = useState(true);
          return (
              <MyContext.Provider value = {value ? 'GOOD' : 'BAD'}>
                  <GrandParent />
                  <button onClick = {() => setValue(!value)}>CLICK ME</button>
              </MyContext.Provider>
          );
      }
    
      export default ContextSample;
    • context를 이용하면, 트리 단계마다 명시적으로 props를 넘겨주지 않아도 많은 컴포넌트가 이러한 값을 공유할 수 있다.

immer library

불변성을 조금 더 수월하게 지킬 수 있도록 도와준다.

const object = {
    a: 1,
    b: 2
};
const nextObject = {
    //1
    ...object,
    b: 3
};
object.b = 3;//2
  1. 해당 객체를 스프레드를 이용하여 복사하고 값을 덮어씌우는 것이 불변성을 지킨다.
  2. 직접 객체에 접근하여 대입하는 것은 불변성을 깨는 행위이다.

배열의 경우 새로운 요소를 생성해 줄 때 위와 같이 복사하고 추가하는 내용들의 코드는 점차 알아보기 힘들어진다.
immer 라이브러리는 불변성을 깨더라도 대신 불변성을 유지해준다.
yarn add immer를 통해 라이브러리를 설치한다.

reducer 정의한 함수를 통해 비교해보자.

  • 기존
      function reducer(state, action){
      switch(action.type){
          return{
              inputs: initialState.inputs,
              users: state.users.concat(action.user)
          }
          case 'TOGGLE_USER':
          return{
              ...state,
              users: state.users.map(
              user => user.id === action.id ? 
              {...user, active: !user.active} : user
              )
          }
          case 'REMOVE_USER':
          return{
              ...state,
              users: state.users.filter(
              user => user.id !== action.id
              )
          }
          default:
          throw new Error('Unhandled Action');
      }
      }
  • immer
      function reducer(state, action){
      switch(action.type){
          case 'CREATE_USER':
          return produce(state, draft => {
              draft.users.push(action.user);
          });
          case 'TOGGLE_USER':
          return produce(state, draft=>{
              const user = draft.users.find(user => user.id == action.id);
              user.active = !user.active;
          });
          case 'REMOVE_USER':
          return produce(state, draft =>{
              const index = draft.users.findIndex(user => user.id == action.id);
              draft.users.splice(index,1);
          });    
          default:
          throw new Error('Unhandled Action');
      }
      }
    • CREATE_USER나 REMOVE_USER같은 경우는 오히려 복잡해졌으나, TOGGLE_USER의 경우 더 보기 편해졌다.
    • TOGGLE_USER와 같이 안의 요소를 수정하거나 조금 더 큰 객체일 경우 쓰는 것을 추천
    • 남발하는것은 좋지 않다.

class 형 컴포넌트

요즘은 잘 쓰지 않으며, 옛날 코드들을 유지보수 할 경우에 쓰는 것을 추천함
또는 함수형 컴포넌트와 Hooks로 작업하지 못하는 경우 사용하는 것을 추천함

  • 사용법

      import React, {Component} from "react";
    
      class Hello extends Component{
          static defaultProps = {
              name: '이름없음'
          }
          render(){
              const {color, isSpecial, name} = this.props
              return (
                  <div style = {color}>
                      {isSpecial && <b>*</b>}
                      안녕하세요 {name}
                  </div>
              )
          }
      }
      export default Hello
  • class 함수 사용

      class Counter extends Component {
          constructor(props){
              super(props);
    
          }
          handleIncrease(){
              console.log("handleIncrease");
          }
          handleDecrease(){
              console.log("handleDecrease");
          }
    
          render(){
              return(
                  <div>
                      <h1>0</h1>
                      <button onClick = {this.handleIncrease}>+1</button>
                      <button onClick = {this.handleDecrease}>-1</button>
                  </div>
              )
          }
      }
    • 단, onClick으로 함수를 보내면 this는 사용이 불가하다.
      • onClick으로 함수를 보낼 때 컴포넌트 인스턴스와 메서드의 관계가 끊기기 때문이다.
      • 이를 방지하기 위해 생성자를 만들어주어 bind를 해준다.
          constructor(props){
              super(props);
              this.handleIncrease = this.handleIncrease.bind(props);
              this.handleDecrease = this.handleDecrease.bind(props);
          }
      • 또는 화살표 함수 문법을 이용한다.
          handleIncrease = () => {
              console.log("handleIncrease");
          }
          handleDecrease = () => {
              console.log("handleDecrease");
          }
  • 상태유지

    • Counter.js

        class Counter extends Component {
            constructor(props){
                super(props);
                this.state = {
                    counter: 0,
                    fixed: 1,
                    updateMe: {
                        toggleMe: false,
                        dontChangeMe: 1,
                    }
                };
            }
            handleIncrease = () => {
                this.setState({
                    counter: this.state.counter+1
                });
            }
            handleDecrease = () => {
                this.setState({
                    counter: this.state.counter-1
                });
            }
      
            render(){
                return(
                    <div>
                        <h1>{this.state.counter}</h1>
                        <button onClick = {this.handleIncrease}>+1</button>
                        <button onClick = {this.handleDecrease}>-1</button>
                        <h6>{this.state.fixed}</h6>
      
                    </div>
                )
            }
        }
      • 기본적으로 state를 수정할 때 바로 수정이 가능하다.
      • 다만, stete가 가지고 있는 객체를 수정할 때 기존 함수 state 수정하듯 객체를 복사해주고 수정해야한다.
          updateMe:{
              ...this.state.updateMe,
              toggleMe: !this.state.updateMe.toggleMe,
          }
        • 불변성을 지키기 위함
    • setState 함수형 업데이트 가능

            handleIncrease = () => {
            this.setState(state => ({
                counter: state.counter+1
            }));
        }
        handleDecrease = () => {
            this.setState(state => ({
                counter: state.counter-1
            }));
        }

      LifeCycle 메서드

      (생명주기 메서드)
      브라우저 상에 컴포넌트가 나타나고 업데이트 되고 사라지게 될 때 호출되는 메서드이다.
      컴포넌트가 에러가 발생했을 때 호출되는 메서드도 이 메서드의 일부이기도 하다.
      이는 class형 컴포넌트에서만 사용이 가능하다.
      (useEffect와 유사함, 작동방식이 다르며, 커버하지 않는 기능도 존재)

각종도구

  • Prettier: 자동으로 코드 스타일을 관리해주는 도구
  • ESlint: 코드를 분석해 문법적인 오류나 안티 패턴을 찾아주고 일관된 코드 스타일로 작성하도록 도와준다.
  • Snippet: 단축키

'WebStudy' 카테고리의 다른 글

React Router  (0) 2021.02.19
React API  (0) 2021.02.18
React 기본 2  (0) 2021.02.13
React 기본 1  (0) 2021.02.12
Web Server 및 WAS  (0) 2021.02.06
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기