ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [React] Section 18, 19
    Frontend/React 2023. 12. 10. 21:35

    Fetch를 통해 데이터 가져오기

    useEffect로 입력한 함수는 Promise를 반환하면 안된다.

    -> async, await를 사용하려면 중첩된 내부 함수로 입력해야함.

    useEffect(() => {
        const fetchMeals = async () => {
          await fetch(
            "https://firebase 경로/meals.json"
          );
        };
      }, []);
    useEffect(() => {
        const fetchMeals = async () => {
          const response = await fetch(
            "https://firebase 경로/meals.json"
          );
          if (!response.ok) {
            throw new Error("Something went wrong!");
          }
          const responseData = await response.json();
    
          const loadedMeals = [];
          for (const key in responseData) {
            loadedMeals.push({
              id: key,
              name: responseData[key].name,
              description: responseData[key].description,
              price: responseData[key].price,
            });
          }
          setMeals(loadedMeals);
          setIsLoading(false);
        };
    
        fetchMeals().catch((error) => {
          setIsLoading(false);
          setHttpError(error.message);
        });
      }, []);

     

    중요한 점: 서버에서는 고객으로부터 받은 데이터를 결코 신뢰해서는 안 된다.

    구축한 검증은 항상 방해받을 수 있기 때문이다.

     

    Redux

    리덕스는 크로스 컴포넌트 또는 앱 와이드 상태를 위한 상태 관리 시스템

     

    우리는 상태를 세 가지로 분류할 수 있다.

    • 로컬 상태
    • 크로스 컴포넌트 상태
    • 앱 와이드 상태

    로컬 상태 : 데이터가 변경되어서 하나의 컴포넌트에 속하는 UI에 영향을 미치는 상태

    우리는 보통 useState를 사용해서 컴포넌트 안에서 로컬 상태를 관리한다. 혹은 약간 복잡하다면 useReducer를 사용할 수도 있다.

     

    크로스 컴포넌트 상태 : 다수의 컴포넌트에 영향을 미치는 상태

    ex) 모달 컴포넌트

    우리는 useState나 useReducer를 이용해서 구현할 수 있다.

    prop chain (prop drilling)을 사용해야 한다.

     

    앱 와이드 상태 : 단지 다수의 컴포넌트가 아니라 애플리케이션의 모든 컴포넌트에 영향을 미치는 상태

    ex) 사용자 인증

     

     

    -> 리액트 컨텍스트와 리덕스는 모두 우리가 그런 크로스 컴포넌트 상태나 앱 와이드 상태를 관리하도록 도와준다.

     

    Redux vs React Context

     

    리액트 컨텍스트는 잠재적인 단점이 몇 개 있다.

    우리는 하나의 애플리케이션에서 컨텍스트와 리덕스를 둘 다 사용할 수도 있다.

     

    잠재적인 단점

    • 리액트 컨텍스트를 사용하면 설정이 아주 복잡해질 수 있고 리액트 컨텍스트를 이용한 상태 관리가 상당히 복잡해질 수 있다. (ContextProvider 컴포넌트가 아주 많이 있게 된다.)
    • 성능 (데이터가 자주 변경되는 경우에는 좋지 않다.)

    소형, 중형, 어떤 대형 애플리케이션에서는 문제가 되지 않을 수도 있다!!!

    그러나 대형 애플리케이션에서 일어날 수 있는 '잠재적인' 문제들이다...

     

    Redux의 작동 방식

    리덕스는 애플리케이션에 있는 '하나의' 중앙 데이터 저장소이다. (여기서 데이터는 state)

    절대로 2개 이상은 갖지 않는다.

    우리는 저장소 전체를 항상 직접 관리할 필요가 없다.

    컴포넌트가 저장소를 구독하고 데이터가 변경될 때마다 저장소가 컴포넌트에게 알려주게 된다.

    컴포넌트는 절대로 저장된 데이터를 직접 조작하지 않는다.

    데이터는 절대로 반대 방향으로 흐르지 않는다. (적어도 직접적인 데이터 흐름은 그렇다.)

    그 대신에 우리는 리듀서라는 개념을 이용한다. 이 함수는 변형을 담당한다.

    액션이 있고 컴포넌트가 액션을 발송한다. 액션은 단순한 자바스크립트 객체이다.

    이 액션이 리듀서가 수행해야 할 작업을 설명한다.

    리덕스는 이 액션을 리듀서로 전달하고 원하는 작업에 대한 설명을 읽게 된다.

     

    const redux = require("redux");
    
    // 최초로 실행될 때, default value가 존재해야 한다.
    const counterReducer = (state = { counter: 0 }, action) => {
      if (action.type === "increment") {
        return {
          counter: state.counter + 1,
        };
      }
      if (action.type === "decrement") {
        return {
          counter: state.counter - 1,
        };
      }
      return state;
    };
    const store = redux.createStore(counterReducer);
    
    const counterSubscriber = () => {
      const latestState = store.getState();
      console.log(latestState);
    };
    
    store.subscribe(counterSubscriber);
    
    store.dispatch({
      type: "increment",
    }); // dispatch는 액션을 발송하는 메소드
    store.dispatch({ type: "decrement" });

     

     

    useSelector

    우리는 useSelector를 사용해서 저장소가 관리하는 데이터에 액세스할 수 있다.

     

    connect

    클래스 기반 컴포넌트를 리덕스에 연결하는 데 도움을 준다.

    함수형 컴포넌트에서도 사용할 수 있지만 함수형 컴포넌트에서는 useDispatch와 useSelector를 쓰는게 더 편리하다!!!

     

    connect를 사용하면 새 함수를 value로 리턴한다.

    export default connect()(Counter);

     

    class Counter extends Component {
      incrementHandler() {
        this.props.increment();
      }
      decrementHandler() {
        this.props.decrement();
      }
      toggleCounterHandler() {}
      render() {
        return (
          <main className={classes.counter}>
            <h1>Redux Counter</h1>
            <div>
              <button onClick={this.incrementHandler.bind(this)}>Increment</button>
              <button onClick={this.decrementHandler.bind(this)}>Decrement</button>
            </div>
            <div className={classes.value}>{this.props.counter}</div>
            <button onClick={this.toggleCounterHandler}>Toggle Counter</button>
          </main>
        );
      }
    }
    const mapStateToProps = (state) => {
      return {
        counter: state.counter,
      };
    };
    const mapDispatchToProps = (dispatch) => {
      return {
        increment: () => dispatch({ type: "increment" }),
        decrement: () => dispatch({ type: "decrement" }),
      };
    };
    export default connect(mapStateToProps, mapDispatchToProps)(Counter);

     

     

     

    Redux state를 올바르게 사용하는 법

    중요한 것은 기존 state와 병합되지 않고 기존 state를 덮어쓴다는 것

    state를 설정할 때는 항상 다른 state를 설정해야 한다.

    중요한 것 : 절대 기존의 state를 변형해서는 안된다.

    대신에, 새로운 state 객체를 반환하여 항상 재정의해야 한다.

     

    Redux에서는 우리가 얻는 원본의 state를 절대 변경해서는 안된다.

     

    1. 특이한 식별자가 있을 때 오타를 내고 싶지 않다면, 상수를 생성한다.

    2. 리듀서를 여러 작은 리듀서로 나눈다.

     

    Redux toolkit이라는 라이브러리

    Redux toolkit은 추가적인 패키지인데 리덕스를 더 편리하고 쉽게 작동할 수 있게 해준다.

    -> 이 패키지를 설치하면, 리덕스 라이브러리를 삭제해야한다. (이미 Redux toolkit에 포함되어 있기 때문에)

     

    Redux toolkit

    createSlice가 createReducer보다 더 강력하다.

    그리고 createSlice는 몇 가지를 한번에 단순화한다.

     

    createSlice({
      name: "counter",
      initialState,
      reducers: {
        increment(state) {
          state.counter++;
        },
        decrement(state) {
          state.counter--;
        },
        increase(state, action) {
          state.counter = state.counter + action.amount;
        },
        toggleCounter(state) {
          state.showCounter = !state.showCounter;
        },
      },
    });

     

     

    보이는 것과 달리 여전히 원래 있는 상태는 절대 변경할 수 없다.

    Redux toolkit의 immer는 자동으로 원래 있는 상태를 복제한다.

     

    configureStore

    createStore처럼 store를 만든다. 그렇지만 다른 점은 여러 개의 리듀서를 하나의 리듀서로 쉽게 합칠 수 있다는 것이다.

    configureStore이 모든 리듀서를 하나의 큰 리듀서로 병합한다.

     

    const store = configureStore({
      reducer: { counter: counterSlice.reducer },
    });

     

    createSlice는 서로 다른 리듀서에 해당하는 고유 액션 식별자를 자동으로 생성한다.

     

    액션 식별자 값을 얻으려면 counterSlice.actions을 사용하면 된다. 이것은 key로 가득한 객체이다.

    counterSlice.actions.toggleCounter

     

    -> 액션 객체를 생성해준다.

     

    const toggleCounterHandler = () => {
        dispatch(counterActions.toggleCounter());
      };
    
      const incrementHandler = () => {
        dispatch(counterActions.increment());
      };
    
      const increaseHandler = () => {
        dispatch(counterActions.increase(10));
      };
      const decrementHandler = () => {
        dispatch(counterActions.decrement());
      };

     

    const counterSlice = createSlice({
      name: "counter",
      initialState,
      reducers: {
        increment(state) {
          state.counter++;
        },
        decrement(state) {
          state.counter--;
        },
        increase(state, action) {
          state.counter = state.counter + action.payload;
        },
        toggleCounter(state) {
          state.showCounter = !state.showCounter;
        },
      },
    });

     

    -> action.payload로 접근해야 한다.

     

    중요한 점은 slice가 여러 개 이더라도 리덕스 스토어는 하나밖에 없다.

    const store = configureStore({
      reducer: {
        counter: counterSlice.reducer,
        auth: authSlice.reducer,
      },
    });

    'Frontend > React' 카테고리의 다른 글

    [React] Section 22  (0) 2024.01.08
    [React] Section 20  (0) 2023.12.17
    [React] Section 17  (0) 2023.11.21
    [React] Section 15, 16  (0) 2023.11.13
    [React] Section 14  (0) 2023.11.12
Designed by Tistory.