Redux 와 React 연결, 비동기 with Redux
Redux 를 React 에 연결 - without 라이브러리
store 를 React 에 연결. react-redux 라이브러리를 안쓰고 자체적으로 연결 가능
단일 store 를 만들고, subscribe 해주고, 변경되는 state 데이터를 얻어 props 로 계속 전달하면 구현 가능
subscribe, unsubscribe 를 연결해서 구현해보겠음
index.js 에서 App 컴포넌트에 store 를 store props 로 내려주고,
App 컴포넌트에서 didmount, willmount 에서 각각 subscribe, unsubscribe 해주겠음 -> useEffect 이용
store 를 props 로 받아오고, state 초기값은 store.getState() 로 설정하고,
state 리턴 시, Object 니까 간단히 JSON.stringify() 로 문자열로 바꿔서 출력함
useEffect [] dependency 에 lint 에러는 store 를 로직 안에서 사용하니까 넣어줘야 한다
return () => unsubscribe 도 넣어준다
dispatch hook 만든 거 가져와서 버튼에 연결해주면, 버튼 누르면 할일 추가도 쭉쭉 된다
컴포넌트에서 subscribe 를 연결해서 안에 state 바꾸는 로직을 적용하면, component 가 store 의 변화에 반응해서 rerender 하는 로직이 만들어지고, store dispatch 함수를 사용하면 component 가 state 를 변경할 수 있게 됨
component 가 store 만 가지면, store 변화에 반응하거나, store 에 변화를 줄 수 있는 component 만들 수 있는 것
그래서 전체 component 에 전달을 위해 context 를 사용하겠음
reduxContext 를 만들고, index.js 에서 App 컴포넌트에 value 로 store 를 전달함 provider 로 사용할 경우 props 는 value 만 넣어줄 수 있음. 그러면 store 가 props 가 아니라 provider 로 전달됨
store 를 useContext Hook 으로 받아오는 걸로 바꿔주면 됨
state 에 관한 로직도 hook 으로 따로 빼면,
context 로 부터 store 를 가져오고, store 로부터 getState 하고, update 일어날 때마다 다시 getState -> setStage 하는 customHook 을 만듬. return state 해서 component 에서 state 설정해주면 됨
dispatch 도 hook 으로 만들 수 있음
그러면 App.js 에는 render 말고 하는 게 없음
todoList 는 state 를 보여주는 역할, todoForm 은 state 를 추가하는 역할
react-redux 를 안 쓰고, redux 와 component 를 연결해봤음
Redux 를 React 에 연결 - with React Redux
react-redux 를 사용해서 redux store 와 react component 를 연결하겠음
redux 는 provider 컴포넌트를 제공함. 이걸로 contextprovider 를 대체 가능하고, value 가 아니라 store props 로 넘김
그리고 connect 라는 HOC 함수를 통해 container 를 만들어줌. container 는 연결한 컴포넌트에 state, dispatch 를 props 로 넣어주는 역할
connect 를 사용하기 위해 ① 어떤 state 를 어떤 props 에 연결할 건지, ② 어떤 dispatch 를 어떤 props 에 연결할 건지, ③ 그 props 를 보낼 컴포넌트를 정의해야 함
store 를 사용할 곳에서 연결해줘야 하고, 연결해주는 HOC 함수가 connect 함수
todoList 에서 HOC 로 store 와 연결하겠음
connect 라는 redux 함수를 가져와서 실행하고, 여기에서 실행한 결과물이 HOC 함수가 됨. connect 함수를 실행한 결과가 함수고, 그 함수를 실행한 결과가 container 임 (??)?(?)??(??)?(??)?(??)?
이렇게 만들어진 container 를 export
connect 함수를 실행할 때 1번째 인자는 config, 2번째 인자는 연결할 component 가 들어가면 됨 (todoList)
config 영역에는 ① store 의 어떤 state 를 받아서 어떤 props 로 넣어줄 거냐 = mapStateToProps, ② state 를 dispatch 하는 어떤 함수를 어떤 props 로 넣어줄 거냐 = mapDispatchToProps 가 들어감
각각 => 함수로 위에서 정의해서 넣어줌
mapStateToProps 는 state 를 받아서 props 객체로 만들면 됨. parameter 가 state => return 값이 props 객체. todos 라는 props 가 들어가고, 내용은 store state 의 todos 가 들어가는 것
mapDispatchToprops 는 parameter 가 dispatch, return 값이 props 객체임.
todoForm 에서 할일 추가하는 dispatch 로직을 넣겠음. { add: (text) parameter 는 text => 로직은 dispatch(액션생성함수(parameter)) , ... } 이런 객체를 return 함
그러면 add 라는 이름의 함수가 넘어감 props.add 실행할 때 add(text) 인자 넣어서 써주면 => dispatch ~ 되는 것
click 내부 로직에서 직접 dispatch 할 필요없이 add 함수를 props 에서 받아서 사용하면 되는 것
이렇게 보면, connect 하는 부분 / connect 안에 든 component (todoForm) 로 나눌 수 있음
connect 안에 든 component 는 이제 그냥 visual component. 함수를 사용하거나 데이터를 받아서 보여주는 역할 = presentational component 혹은 component
connect 하는 부분은 container 혹은 smart component
역할이 완벽 분리된 것임
store 와 presentational component 를 이어주는 역할 : container
container 가 주는 data, 함수를 받아서 보여주거나 함수를 실행하는 역할만 : presentational component
아예 파일을 분리시켜버리면, presentational component 에는 redux 의 흔적이 전혀 없음. 그냥 props 를 받아서 함수를 실행하는 component 가 됨
container 파일에서 presentational component import 해서 같이 export 함
지금까지 connect 라는 HOC 함수로 연결했는데, HOC 로 공통로직을 제공하던 부분이 Hook 으로 많이 바뀌었음
hook 으로 다시 작성해보겠음
todoList Container 는 그냥 store 를 연결하고, state 를 props 로 전달하는 역할만 함
그래서 redux 에서 제공하는 hook 인 useSelector 이용함. useSelector 의 parameter 로 함수가 들어가는데, (state) => state.xxx . state 의 어떤 값을 return 할 것인지를 적어줌
useSelector 결과를 const 변수로 받아서 그걸 component 에 props 로 넣어줌
HOC 를 이용했던 게 그냥 hook 을 이용해서 하위 컴포넌트에 props 를 넣어주는 읽기쉬운 코드가 됨
todoForm Container 에서도 똑같이. dispatch 를 추가하는데 역시 redux 에서 제공하는 useDispatch hook 을 이용함
useDispatch hook 의 결과물이 dispatch 함수니까 그걸 const 변수로 받아서 component 에 props 로 넣어줌
근데 매번 render 할 때마다 새로운 함수를 만드므로, useCallback 함수로 감싸줌. dependency 는 언제 add 함수가 새로 바뀌는 지를 쓰면 되는데, dispatch 함수가 바뀌었을 때니까 dispatch (store 내부 dispatch 함수 바뀔 일 거의 없음)
보여주는 부분: store 등장할 필요도 없이 (provider 로 제공) useSelector hook 으로 특정 state 리턴
변경하는 부분: useDispatch hook 으로 여러 함수 전달 가능
redux 를 이용해서 connect 라는 HOC / useSelector, useDispatch hook 으로 container 를 만들고, component 에 props 로 전달하는 방식에 대해 알아봤음
redux 를 사용할 때 container 와 component 를 명확히 구분하고 역할에 맞는 로직을 작성하는 게 좋음
Async Action with Redux
redux 에서 비동기 처리를 위해 어떻게 action 을 처리하는지 보겠음
actions 에서 ① type, ② action 생성함수를 정의함. action constructor 에서 받아올 parameter 를 있으면 쓰고, return 으로 정해진 type 쓰고, 인자를 고대로 넘겨줌. return action 하는 것임. action 의 형태는 다양함
그리고 reducer 에서 type 별로 분기만 해주고, 전체 parameter 는 state, action 으로 정해져 있는 것임. action 의 형태는 다양하니까 type 별 분기 내에서 action.xxx 해서, 그 type 에서 받아온 action 에만 있는 그 인자에 접근하는 것임
action 생성함수는 pure function 이니까 api 에서 data 받아오고 하는 거는 container 에서 처리하고, 그 결과만 action 생성함수에 집어넣어 주는 것임. 집어넣어서 dispatch 해주는 것임
actions 에 user data 받아오는 로직 관련해 action 및 생성함수를 추가하겠음
start 는 loading 시작, success 는 loading 끝내고 data 세팅, fail 은 loading 끝내고 error 세팅함
이에 맞춰서 initialState 를 객체 {} 로 만들고, loading false, data [], error null 추가함
reducer 에서 initialState 세팅해주는 것이고, action type 에 따라 state 변경을 해주는 것임. action 은 그냥 type 전달 및 필요한 parameter 전달의 역할만 한다고 생각하자
App 에서 user 목록을 보여주는 component userList 와 Container 를 만듬. userList 에 length == 0 조건도 추가
map 돌릴 때 key 도 추가해주기
API 호출은 componentDidmount 시점에 해야되고, useEffect 안에서 함. axios 설치해서 비동기 await 사용을 위해 내부에 함수를 async 로 선언하고 바로 아래에서 다시 호출함
그런데 이렇게 하면 presentational component 에 로직이 들어가서 복잡해짐. 단순 호출을 위한 success, start, fail 함수를 props 로 전달해서 useEffect dependency 만 복잡해짐
Container 에서 start, success, fail 전달할 때는 useCallback 쓰고 dependency dispatch 로 넘기는데 다소 복잡함
이 비동기 로직 API 호출을 Container 에서 하는 걸로만 바꾸겠음
( dispatch hook 만 쓰고 action 생성함수는 따로 뭐 처리할 필요없이 import 해서 호출만 하면 됨 )
start, success, fail 각각 전달할 필요없이 getUsers 함수를 만들어서 그 안에 로직 넣어서 전달함. getUsers 도 역시 반복적으로 만들어지므로 useCallback 쓰고, dependency 역시 dispatch 밖에 없음
component 는 그냥 props 를 받아서 보여주는 용도로만 사용하고, Container 에서 비동기 처리를 하는 함수를 만들면 훨씬 깔끔하고 역할에 맞는 코드가 작성됨
component 에서 didmount 부분에 데이터 받아오는 건 해야되니까 그 메소드만 받아서 life cycle 에 맞게 메소드만 호출해주는 거임
Redux MiddleWare
Middleware 는 dispatch 의 앞 뒤에 코드를 추가할 수 있게 해줌
store 를 만들 때 middleware 를 설정하는 부분이 -> createStore 함수 사용할 때 enhancer 부분에 넣어주면 됨
enhancer 부분이 곧 middleware
dispatch 가 호출될 때 middleware 를 통과하는 부분에서 원하는 대로 작성할 수 있음. store 로 가고 있는 action 을 가로채서 변조할 수도 있고, 못가게 할 수도 있고 여러가지 등등
*패스트 캠퍼스 프론트엔드 강의를 듣고 정리함