react-redux를 쓰고 연결하기
Provider
컴포넌트 제공Container
를 만들어줌 (HOC는 보통 로직을 만들어서 props로 넣어줌) state
와 dispatch
(action)를 연결한 컴포넌트에 props로 넣어주는 역할그렇다면 필요한 것은?
- 어떤 state를 어떤 props에 연결할 것인지에 대한 정의
- 어떤 dispatch(action)을 어떤 props에 연결할 것인지에 대한 정의
- 그 props를 보낼 컴포넌트를 정의
react-redux의 Provider import 후 Provider 컴포넌트로 변경
src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import store from './redux/store';
import { Provider } from 'react-redux';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider value={store}>
<App />
</Provider>
</React.StrictMode>
);
HOC로 TodoList 연결하기
src/components/TodoList.jsx
import { connect } from 'react-redux';
// 위쪽으로 이동
// Props로 todos를 받아서 map
function TodoList({todos}) {
return (
<ul>
{todos.map((todo)=>{
return <li>{todo.text}</li>;
})}
</ul>
);
}
// react-redux의 함수 사용하기 위해 가져와서 실행
// 실행한 결과물이 HOC 함수가 되므로 다시 한번 실행
// 실행한 결과가 Container
// 만들어진 TodoListContainer를 export default
// state 객체를 받아서 Props로 만들기
const mapStateToProps = (state) => {
return {
todos: state.todos,
};
};
// dispatch 객체를 받아서 Props로 만들기
const mapDispatchToProps = (dispatch) => {
return {};
};
// connect가 config하는 인자 2개 함수
// HOC의 인자
const TodoListContainer = connect(mapStateToProps,mapDispatchToProps)(TodoList);
export default TodoListContainer;
src/components/TodoForm.jsx
import { useRef } from "react";
import { connect } from 'react-redux';
import { addTodo } from "../redux/actions";
// 위쪽으로 이동
// TodoForm은 함수를 받아서 보여주는 역할(Presentational Component)
// Container가 주는 data를 받아서 그냥 보여주거나 실행함
function TodoForm({ add }) {
const inputRef = useRef();
return (
<div>
<input ref={inputRef} /> <button onClick={click}>추가</button>
</div>
);
// 버튼 클릭 시 addTodo 액션
// dispatch
function click() {
// 직접 dispatch하지 않고 add를 props으로 받아서 실행
add(inputRef.current.value);
}
}
// Container = Smart Component로 역할 분리
// Store와 Presentatinal Component를 이어주는 역할 = connect
export default connect(
(state) => ({}),
// todos를 실행하는 로직
(dispatch) => ({
add: (text) => {
dispatch(addTodo(text));
}
})
)(TodoForm);
src/containers/TodoFormContainer.jsx
import { connect } from 'react-redux';
import { addTodo } from "../redux/actions";
import TodoForm from '../components/TodoForm';
// 정확한 Container로 분리
// Container = Smart Component로 역할 분리
// Store와 Presentatinal Component를 이어주는 역할 = connect
const TodoFormContainer = connect(
(state) => ({}),
// todos를 실행하는 로직
(dispatch) => ({
add: (text) => {
dispatch(addTodo(text));
}
})
)(TodoForm);
export default TodoFormContainer;
src/components/TodoForm.jsx
import { useRef } from "react";
export default function TodoForm({ add }) {
const inputRef = useRef();
return (
<div>
<input ref={inputRef} /> <button onClick={click}>추가</button>
</div>
);
function click() {
add(inputRef.current.value);
}
}
src/containers/TodoListContainer.jsx
import { connect } from 'react-redux';
import TodoList from '../components/TodoList';
const mapStateToProps = (state) => {
return {
todos: state.todos,
};
};
const mapDispatchToProps = (dispatch) => {
return {};
};
const TodoListContainer = connect(mapStateToProps,mapDispatchToProps)(TodoList);
export default TodoListContainer;
src/components/TodoList.jsx
export default function TodoList({todos}) {
return (
<ul>
{todos.map((todo)=>{
return <li>{todo.text}</li>;
})}
</ul>
);
}
App.js로 가서 컨테이너로 수정
src/App.js
import logo from './logo.svg';
import './App.css';
import TodoListContainer from './containers/TodoListContainer';
import TodoFormContainer from './containers/TodoFormContainer';
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<TodoListContainer />
<TodoFormContainer />
</header>
</div>
);
}
export default App;
HOC로 공통로직을 제공하는 것도 Hook으로 많이 바뀌었음
src/containers/TodoListContainer.jsx
import { useSelector } from 'react-redux';
import TodoList from '../components/TodoList';
// jsx를 리턴하는 컴포넌트
// TodoListContainer가 하는 일 : store 연결 > data 꺼내서 > 필요한 것을 props로 넣어주기
// useSelector hook을 사용하면 좀 더 편하게 하는 일을 명시적으로 보여줄 수 있음
// 하위 컴포넌트에 찔러넣어주는 형태로 변경
function TodoListContainer() {
const todos = useSelector((state)=>state.todos);
return <TodoList todos={todos} />;
}
export default TodoListContainer;
src/containers/TodoFormContainer.jsx
import { useCallback } from "react";
import { addTodo } from "../redux/actions";
import { useDispatch } from 'react-redux';
import TodoForm from '../components/TodoForm';
// useDispatch hook 활용
// 실행될 때마다 add에 새로운 함수를 찔러넣어주므로
// useCallback으로 처리 (dispatch 함수가 deps하여 새로 만들어질 때 다시 만들어짐)
// store가 달라지지 않으므로 처음에 만든 것과 똑같은 형식 (불필요하게 새로 만들어지지 x)
export default function TodoFormContainer() {
const dispatch = useDispatch();
const add = useCallback((text)=>{
dispatch(addTodo(text));
}, [dispatch]);
return <TodoForm add={add} />
};
결과
참고 자료 & 공부 (개념 이해)