일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 참조형변수
- GRANT VIEW
- 대덕인재개발원
- EnhancedFor
- abstract
- 자동차수리시스템
- 오라클
- exception
- 컬렉션 타입
- 메소드오버로딩
- Java
- 컬렉션프레임워크
- 추상메서드
- cursor문
- 사용자예외클래스생성
- 자바
- 예외미루기
- 제네릭
- 어윈 사용법
- 다형성
- 예외처리
- oracle
- 인터페이스
- 환경설정
- 한국건설관리시스템
- 집합_SET
- 생성자오버로드
- NestedFor
- 객체 비교
- 정수형타입
- Today
- Total
거니의 velog
(8) 리덕스를 사용하여 리액트 애플리케이션 상태 관리하기 3 본문
5. 컨테이터 컴포넌트 만들기
* 이제는 컴포넌트에서 리덕스 스토어에 접근하여 원하는 상태를 받아 오고, 또 액션도 디스패치해 줄 차례이다. 리덕스 스토어와 연동된 컴포넌트를 컨테이너 컴포넌트라고 부른다.
(1) CounterContainer 만들기
* src 디렉터리에 containers 디렉터리를 생성하고, 그 안에 CounterContainer 컴포넌트를 만들자.
import React from 'react';
import Counter from '../components/Counter';
const CounterContainer = () => {
return <Counter />;
};
export default CounterContainer;
* 위 컴포넌트를 리덕스와 연동하려면 react-redux에서 제공하는 connect 함수를 사용해야 한다. 이 함수는 다음과 같이 사용한다.
connect(mapStateToProps, mapDispatchToProps)(연동할 컴포넌트)
* 여기서 mapStateToProps는 리덕스 스토어 안의 상태를 컴포넌트의 props로 넘겨주기 위해 설정하는 함수이고, mapDispatchToProps는 액션 생성 함수를 컴포넌트의 props로 넘겨주기 위해 사용하는 함수이다.
* 이렇게 connect 함수를 호출하고 나면 또 다른 함수를 반환한다. 반환된 함수에 컴포넌트를 파라미터로 넣어 주면 리덕스와 연동된 컴포넌트가 만들어진다.
* 위 코드를 더 쉽게 풀면 다음과 같은 형태이다.
const makeContainer = connect(mapStateToProps, mapDispatchToProps)
makeContainer(타깃 컴포넌트)
* 자, 이제 CounterContainer 컴포넌트에서 connect를 사용해 보자.
[CounterContainer.js]
import React from 'react';
import { connect } from 'react-redux';
import Counter from '../components/Counter';
const CounterContainer = ({ number, increase, decrease }) => {
return (
<Counter number={number} onIncrease={increase} onDecrease={decrease} />
);
};
const mapStateToProps = (state) => ({
number: state.counter.number,
});
const mapDispatchToProps = (dispatch) => ({
// 임시 함수
increase: () => {
console.log('increase');
},
decrease: () => {
console.log('decrease');
},
});
export default connect(mapStateToProps, mapDispatchToProps)(CounterContainer);
* mapStateToProps와 mapDispatchToProps에서 반환하는 객체 내부의 값들은 컴포넌트의 props로 전달된다. mapStateToProps는 state를 파라미터로 받아 오며, 이 값은 현재 스토어가 지니고 있는 상태를 가리킨다. mapDispatchToProps의 경우 store의 내장 함수 dispatch를 파라미터로 받아온다. 현재 mapDispatchToProps에서는 진행 절차를 설명하기 위해 임시로 console.log()를 사용하고 있다.
* 다음으로 App에서 Counter를 CounterContainer로 교체하자.
[App.js]
import React from 'react';
import Todos from './components/Todos';
import CounterContainer from './containers/CounterContainer';
const App = () => {
return (
<div>
<CounterContainer />
<hr />
<Todos />
</div>
);
};
export default App;
* 브라우저를 열어서 +1, -1 버튼을 눌러 보자. 콘솔에 increase와 decrease가 찍히면 된다.
* 자, 이번에는 console.log() 대신 액션 생성 함수를 불러와서 액션 객체를 만들고 디스패치해 주겠다.
[CounterContainer.js]
import React from 'react';
import { connect } from 'react-redux';
import Counter from '../components/Counter';
import { increase, decrease } from '../modules/counter';
const CounterContainer = ({ number, increase, decrease }) => {
return (
<Counter number={number} onIncrease={increase} onDecrease={decrease} />
);
};
const mapStateToProps = (state) => ({
number: state.counter.number,
});
const mapDispatchToProps = (dispatch) => ({
increase: () => {
dispatch(increase());
},
decrease: () => {
dispatch(decrease());
},
});
export default connect(mapStateToProps, mapDispatchToProps)(CounterContainer);
* 이제 다시 +1, -1 버튼을 눌러 보자. 숫자가 바뀌는가? 리덕스 개발자 도구도 한번 확인해 보자.
* connect 함수를 사용할 때는 일반적으로 위 코드와 같이 mapStateToProps와 mapDispatchToProps를 미리 선언해 놓고 사용한다. 하지만 connect 함수 내부에 익명 함수 형태로 선언해도 문제되지 않는다. 어떻게 보면 코드가 더 깔끔해지기도 하는데, 취향에 따라 다음과 같이 작성해도 된다.
import React from 'react';
import { connect } from 'react-redux';
import Counter from '../components/Counter';
import { increase, decrease } from '../modules/counter';
const CounterContainer = ({ number, increase, decrease }) => {
return (
<Counter number={number} onIncrease={increase} onDecrease={decrease} />
);
};
export default connect(
(state) => ({
number: state.counter.number,
}),
(dispatch) => ({
increase: () => dispatch(increase()),
decrease: () => dispatch(decrease()),
}),
)(CounterContainer);
* 위 코드에서는 액션 생성 함수를 호출하여 디스패치하는 코드가 한 줄이기 때문에 불필요한 코드 블록을 생략해 주었다. 다음 두 줄의 코드는 작동 방식이 완전히 같다.
increase: () => dispatch(increase()),
increase: () => { return dispatch(increase()) },
* 컴포넌트에서 액션을 디스패치하기 위해 각 액션 생성 함수를 호출하고 dispatch로 감싸는 작업이 조금 번거로울 수도 있다. 특히 액션 생성 함수의 개수가 많아진다면 더더욱 그럴 것이다. 이와 같은 경우에는 리덕스에서 제공하는 bindActionCreators 유틸 함수를 사용하면 간편하다. 한번 사용해 보자.
[CounterContainer.js]
import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import Counter from '../components/Counter';
import { increase, decrease } from '../modules/counter';
const CounterContainer = ({ number, increase, decrease }) => {
return (
<Counter number={number} onIncrease={increase} onDecrease={decrease} />
);
};
export default connect(
(state) => ({
number: state.counter.number,
}),
(dispatch) =>
bindActionCreators(
{
increase,
decrease,
},
dispatch,
),
)(CounterContainer);
* 브라우저를 열어서 조금 전과 똑같이 작동하는지 확인해 보자.
* 방금 작성한 방법보다 한 가지 더 편한 방법이 있다. 바로 mapDispatchToProps에 해당하는 파라미터를 함수 형태가 아닌 액션 생성 함수로 이루어진 객체 형태로 넣어 주는 것이다. 다음과 같이 말이다.
import React from 'react';
import { connect } from 'react-redux';
import Counter from '../components/Counter';
import { increase, decrease } from '../modules/counter';
const CounterContainer = ({ number, increase, decrease }) => {
return (
<Counter number={number} onIncrease={increase} onDecrease={decrease} />
);
};
export default connect(
(state) => ({
number: state.counter.number,
}),
{
increase,
decrease,
},
)(CounterContainer);
* 위와 같이 두 번째 파라미터를 아예 객체 형태로 넣어 주면 connect 함수가 내부적으로 bindActionCreators 작업을 대신해 준다.
(2) TodosContainer 만들기
* 이번에는 Todos 컴포넌트를 위한 컨테이너인 TodosContainer를 작성해 보겠다. CounterContainer를 만들 때 배웠던 connect 함수를 사용하고, mapDispatchToProps를 짧고 간단하게 쓰는 방법을 적용해서 코드를 작성해 보자.
[TodosContainer.js]
import React from 'react';
import { connect } from 'react-redux';
import { changeInput, insert, toggle, remove } from '../modules/todos';
import Todos from '../components/Todos';
const TodosContainer = ({
input,
todos,
changeInput,
insert,
toggle,
remove,
}) => {
return (
<Todos
input={input}
todos={todos}
onChangeInput={changeInput}
onInsert={insert}
onToggle={toggle}
onRemove={remove}
/>
);
};
export default connect(
// 비구조화 할당을 통해 todos를 분리하여
// state.todos.input 대신 todos.input을 사용
({ todos }) => ({
input: todos.input,
todos: todos.todos,
}),
{
changeInput,
insert,
toggle,
remove,
},
)(TodosContainer);
* 이전에 todos 모듈에서 작성했던 액션 생성 함수와 상태 안에 있던 값을 컴포넌트의 props로 전달해 주었다.
* 컨테이너 컴포넌트를 다 만든 후에는 App 컴포넌트에서 보여 주던 Todos 컴포넌트를 TodosContainer 컴포넌트로 교체하자.
[App.js]
import React from 'react';
import CounterContainer from './containers/CounterContainer';
import TodosContainer from './containers/TodosContainer';
const App = () => {
return (
<div>
<CounterContainer />
<hr />
<TodosContainer />
</div>
);
};
export default App;
* 그 다음에는 Todos 컴포넌트에서 받아 온 props를 사용하도록 구현해 보자.
[components/Todos.js]
import React from 'react';
const TodoItem = ({ todo, onToggle, onRemove }) => {
return (
<div>
<input
type="checkbox"
onClick={() => onToggle(todo.id)}
checked={todo.done}
readOnly={true}
/>
<span style={{ textDecoration: todo.done ? 'line-through' : 'none' }}>
{todo.text}
</span>
<button onClick={() => onRemove(todo.id)}>삭제</button>
</div>
);
};
const Todos = ({
input, // 인풋에 입력하는 텍스트
todos, // 할 일 목록이 들어 있는 객체
onChangeInput,
onInsert,
onToggle,
onRemove,
}) => {
const onSubmit = (e) => {
e.preventDefault();
onInsert(input);
onChangeInput(''); // 등록 후 인풋 초기화
};
const onChange = (e) => onChangeInput(e.target.value);
return (
<div>
<form onSubmit={onSubmit}>
<input value={input} onChange={onChange} />
<button type="submit">등록</button>
</form>
<div>
{todos.map((todo) => (
<TodoItem
todo={todo}
key={todo.id}
onToggle={onToggle}
onRemove={onRemove}
/>
))}
</div>
</div>
);
};
export default Todos;
* 이제 모든 작업이 끝났다. 브라우저에서 할 일 목록 기능이 잘 작동하는지 확인해 보자. 일정을 새로 추가해 보고, 체크 박스도 눌러 보고, 삭제도 해 보자.
* 모든 기능이 잘 작동하는가?
'React > React_리액트 심화' 카테고리의 다른 글
(10) 리덕스를 사용하여 리액트 애플리케이션 상태 관리하기 5 (0) | 2023.12.13 |
---|---|
(9) 리덕스를 사용하여 리액트 애플리케이션 상태 관리하기 4 (0) | 2023.12.13 |
(7) 리덕스를 사용하여 리액트 애플리케이션 상태 관리하기 2 (0) | 2023.12.13 |
(6) 리덕스를 사용하여 리액트 애플리케이션 상태 관리하기 1 (0) | 2023.12.13 |
(5) 리덕스 라이브러리 이해하기 2 (0) | 2023.12.13 |