잼무비

필터링 우여곡절 -1-

잼굴 2022. 12. 22. 19:33

후....

 

 

 

할일

 

영화제목, 개봉년도, 평균 평점 , 장르를 설정하는 컴포넌트들을 작성했다.

각 컴포넌트들은 각자 movieFilterReducer를 통해 state에 값을 저장하고 있으며,

state에 저장된 값들을 전달인자로 movieFilterActions의 getFilteredMovies 함수를 통해 GET 요청을 하도록 한다.

 

 

문제 1.

movieFilterReducer의 state에 저장된 각 필터링 값들을 movieFilterAction의 action에서 GET요청을 하기전에, redux의 useSelector를 통해 전달받으려 하였고 에러가 발생했다.

 

에러:

React Hook "useSelector" cannot be called at the top level
React Hooks must be called in a React function component or a custom React Hook function

내 코드:

import { useSelector } from "react-redux";
import api from "../api";

const API_KEY = process.env.REACT_APP_API_KEY;

const {
  keyword,
  sortBy,
  withGenres,
  includeVideo,
  releaseDateGte,
  releaseDateLte,
  voteAverageGte,
  voteAverageLte,
} = useSelector((state) => state.movieFilterActions); // Error occurred

function getFilteredMovies(
  keyword,
  sortBy,
  withGenres,
  includeVideo,
  releaseDateGte,
  releaseDateLte,
  voteAverageGte,
  voteAverageLte
) {
  return async (dispatch) => {
    try {
      dispatch({ type: "GET_FILTERED_MOVIES_REQUEST" });

      const FilteredMovies = await api.get(
        `/discover/movie?api_key=${API_KEY}&language=en-US&page=1${
          keyword ? `&with_text_query=${keyword}` : ""
        }${sortBy ? `&sort_by=${sortBy}` : ""}${
          includeVideo ? `&include_video=${includeVideo}` : ""
        }${releaseDateGte ? `&release_date.gte=${releaseDateGte}` : ""}${
          releaseDateLte ? `&release_date.lte=${releaseDateLte}` : ""
        }${voteAverageGte ? `&vote_average.gte=${voteAverageGte}` : ""}${
          voteAverageLte ? `&vote_averag.lte=${voteAverageLte}` : ""
        }`
      );

      dispatch({
        type: "GET_FILTERED_MOVIES_SUCCESS",
        payload: {
          FilteredMoviesJson: FilteredMovies,
        },
      });
    } catch (error) {
      dispatch({ type: "GET_FILTERED_MOVIES_FAILURE", payload: { error } });
    }
  };
}

export const movieFilterActions = {
  getFilteredMovies,
};

 

분석한 원인:

에러 메시지처럼 useSelector는 React function component 내부나, custom React Hook function에서만 불러와 진다.

내가 시도한 방법은 일반 function에서 훅을 사용하려한거였다.

 

 

첫번째 해결시도 

일반 JS function으로 작성한 api request action을 React function으로 전환 한 뒤,
다시 useSelector Hook을 사용해야겠다고 생각을 함

 

import { useSelector } from "react-redux";
import api from "../api";

const API_KEY = process.env.REACT_APP_API_KEY;

export default function GetFilteredMovies() {
  const {
    keyword,
    sortBy,
    withGenres,
    includeVideo,
    releaseDateGte,
    releaseDateLte,
    voteAverageGte,
    voteAverageLte,
  } = useSelector((state) => state.movieFilterActions);

  return async (dispatch) => {
    try {
      dispatch({ type: "GET_FILTERED_MOVIES_REQUEST" });

      const FilteredMovies = await api.get(
        `/discover/movie?api_key=${API_KEY}&language=en-US&page=1${
          keyword ? `&with_text_query=${keyword}` : ""
        }${sortBy ? `&sort_by=${sortBy}` : ""}${
          includeVideo ? `&include_video=${includeVideo}` : ""
        }${releaseDateGte ? `&release_date.gte=${releaseDateGte}` : ""}${
          releaseDateLte ? `&release_date.lte=${releaseDateLte}` : ""
        }${voteAverageGte ? `&vote_average.gte=${voteAverageGte}` : ""}${
          voteAverageLte ? `&vote_averag.lte=${voteAverageLte}` : ""
        }`
      );

      dispatch({
        type: "GET_FILTERED_MOVIES_SUCCESS",
        payload: {
          FilteredMoviesJson: FilteredMovies,
        },
      });
    } catch (error) {
      dispatch({ type: "GET_FILTERED_MOVIES_FAILURE", payload: { error } });
    }
  };
}
Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app

근데 에러가 또 뜸.. Invalid hook call

 

movieFilterAction에서 useSelector hook을 이용해서 한번에 데이터를 받아오는 방식이 문제가 있다고 판단했다.

 

두번째 해결시도 

필터링을 담당하는 각 컴포넌트에서 useSelector hook을 이용해서 데이터를 받아오고, 그걸 movieFilterAction에 전달인자로 전달해서 api call을 실행하는 방식으로 접근했다.

// MovieFilterInput 컴포넌트 예시
import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import { movieFilterActions } from "../redux/actions/movieFilterActions";

const MovieFilterInput = ({ show }) => {
  useEffect(() => {
    dispatch({ type: "RESET_FILTERED_MOVIES_STORE_SUCCESS" });
  }, []);
  
  const [
    keyword,
    sortBy,
    withGenres,
    includeVideo,
    releaseDateGte,
    releaseDateLte,
    voteAverageGte,
    voteAverageLte,
  ] = useSelector((state) => [
    state.movieFilter.keyword,
    state.movieFilter.sortBy,
    state.movieFilter.withGenres,
    state.movieFilter.includeVideo,
    state.movieFilter.releaseDateGte,
    state.movieFilter.releaseDateLte,
    state.movieFilter.voteAverageGte,
    state.movieFilter.voteAverageLte,
  ]);

  const dispatch = useDispatch(); 
  const [select, setSelect] = React.useState("ALL");

  const handleselect = (event, newSelect) => {
    setSelect(newSelect);
  };

  const test = (toggle) => {
    dispatch({
      type: "INCLUDE_MOVIE_VIDEO_TOGGLE_SUCCESS",
      payload: toggle,
    });
  };

  return (
    <>
      <ThemeProvider theme={theme}>
        <div className="searchBar">
          <h2>SEARCH</h2>
          <ToggleButtonGroup
            color="primary"
            value={select}
            exclusive
            onChange={handleselect}
            size="small"
            aria-label="Include Movie Video"
          >
            <ToggleButton
              value="ALL"
              aria-label="ALL"
              onClick={() => test(false)}
            >
              ALL
            </ToggleButton>
            <ToggleButton
              value="Include Movie Video"
              aria-label="Include Movie Video"
              onClick={() => test(true)}
            >
              Include Movie Video
            </ToggleButton>
          </ToggleButtonGroup>
        </div>
        <TextField
          id="search_input"
          variant="filled"
          label="Movie Title"
          color="primary"
          sx={{ width: "310px" }}
          onKeyPress={(e) => {
            if (e.key === "Enter") {
              show(false);
              dispatch({
                type: "SEARCH_KEYWORD_STORE_SUCCESS",
                payload: e.target.value,
              });

              dispatch(
                movieFilterActions.getFilteredMovies(
                  keyword,
                  sortBy,
                  withGenres,
                  includeVideo,
                  releaseDateGte,
                  releaseDateLte,
                  voteAverageGte,
                  voteAverageLte
                )
              );
            }
          }}
        />
      </ThemeProvider>
    </>
  );
};

 

// movieFilterActions 예시

import api from "../api";
const getFilteredMovies = (
  keyword,
  sortBy,
  withGenres,
  includeVideo,
  releaseDateGte,
  releaseDateLte,
  voteAverageGte,
  voteAverageLte
) => {
  
  const API_KEY = process.env.REACT_APP_API_KEY;

  return async (dispatch) => {
    try {
      dispatch({ type: "GET_FILTERED_MOVIES_REQUEST" });

      const FilteredMovies = await api.get(
        `/discover/movie?api_key=${API_KEY}&language=en-US&page=1${
          keyword ? `&with_text_query=${keyword}` : ""
        }${
          includeVideo ? `&include_video=${includeVideo}` : ""
        }${releaseDateGte ? `&release_date.gte=${releaseDateGte}` : ""}${
          releaseDateLte ? `&release_date.lte=${releaseDateLte}` : ""
        }${voteAverageGte ? `&vote_average.gte=${voteAverageGte}` : ""}${
          voteAverageLte ? `&vote_average.lte=${voteAverageLte}` : ""
        }`
      );

      dispatch({
        type: "GET_FILTERED_MOVIES_SUCCESS",
        payload: {
          FilteredMoviesJson: FilteredMovies,
        },
      });
    } catch (error) {
      dispatch({ type: "GET_FILTERED_MOVIES_FAILURE", payload: { error } });
    }
  };
};
export const movieFilterActions = {
  getFilteredMovies,
};

이렇게 하니 다중필터값들을 만족 시키면서 해당 데이터를 GET 할수있었다.