0. 작업환경설정
1. UI 구성
2. 프레젠테이셔널 컴포넌트 생성
3. 리덕스 생성
4. 리액트 앱에 리덕스 적용
5. 컨테이너 컴포넌트 생성
6. redux-actions라이브러리 적용
0. 작업환경설정
$ yarn create react-app react-redux-tutorial
$ cd react-redux-tutorial
$ yarn add redux react-redux
// redux 라이브러리 : createStore함수를 사용하여 스토어생성
// react-redux 라이브러리 : connect함수와 Provider컴포넌트를 사용하여 리액트에서 리덕스 관련작업 처리
1. UI 구성
- 프레젠테이셔널 컴포넌트 : 상태관리가 이루어지지 않고, 그저 props를 받아 와서 화면에 UI를 보여 주기만 하는 컴포넌트
- 컨테이너 컴포넌트 : 리덕스와 연동되어 있는 컴포넌트. 리덕스로 부터 상태를 받아오기도 하고 리덕스 스토어에 액션을 디스패치 함.
2. 프레젠테이셔널 컴포넌트 생성
// src/components/Todos.js
import React from 'react';
const TodoItem = ({ todo, onToggle, onRemove }) => {
return (
<div>
<input type="checkbox" />
<span>text</span>
<button>remove</button>
</div>
);
};
const Todos = ({
input,
todos,
onChangeInput,
onInsert,
onToggle,
onRemove,
}) => {
const onSubmit = e => {
e.preventDefault();
};
return (
<>
<form onSubmit={onSubmit}>
<input />
<button type="submit">register</button>
</form>
<div>
<TodoItem />
<TodoItem />
<TodoItem />
<TodoItem />
</div>
</>
);
};
export default Todos;
// src/App.js
import React from 'react';
import Todos from './components/Todos';
import './App.css';
function App() {
return (
<div>
<Todos />
</div>
);
}
export default App;
3. 리덕스 생성
- 일반적인 디렉토리 구조 : actions, constatns, reducers 3개의 디렉토리를 만들고 그안에 기능별로 파일을 하나씩 만든다
- Ducks패턴 : 액션타입, 액션생성함수, 리듀서 모두를 '모듈'이라는 하나의 파일에 몰아서 작성하는 방식
// 모듈 생성
// src/modules/Todos.js : Ducks패턴
//action type
const CHANGE_INPUT = 'todos/CHANGE_INPUT';
const INSERT = 'todos/INSERT';
const TOGGLE = 'todos/TOGGLE';
const REMOVE = 'todos/REMOVE';
//action creator
export const changeInput = input => ({
type: CHANGE_INPUT,
input,
});
let id = 3; //insert가 호출될 때마다 1씩 증가
export const insert = text => ({
type: INSERT,
todo: {
id: id++,
text,
done: false,
},
});
export const toggle = id => ({
type: TOGGLE,
id,
});
export const remove = id => ({
type: REMOVE,
id,
});
// initial state
const initialState = {
input: '',
todos: [
{
id: 1,
text: 'todo job 1',
done: true,
},
{
id: 2,
text: 'todo job2',
done: false,
},
],
};
//reducer
function todos(state = initialState, action) {
switch (action.type) {
case CHANGE_INPUT:
return {
...state,
input: action.input,
};
case INSERT:
return {
...state,
todos: state.todos.concat(action.todo), //concat : 배열에 원소추가
};
case TOGGLE:
return {
...state,
todos: state.todos.map(todo =>
todo.id === action.id ? { ...todo, done: !todo.done } : todo,
),
};
case REMOVE:
return {
...state,
todos: state.todos.filter(todo => todo.id !== action.id), // filter의 파라미터로 전달된 함수의 조건을 만족하는 요소들로만 구성된 새로운 배열 반환
};
default:
return state;
}
}
export default todos;
// 루트 리듀서 생성
// src/modules/index.js
import { combineReducers } from 'redux';
import Todos from './Todos';
// 스토어를 만들 때는 리듀서를 하나만 사용해야 하므로
// 리듀서가 여러개인 경우 combinereducers함수를 통해 하나로 묶는다.
const rootReducer = combineReducers({
Todos,
});
export default rootReducer;
4. 리액트 앱에 리덕스 적용
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { createStore } from 'redux';
import rootReducer from './modules';
import { Provider } from 'react-redux';
const store = createStore(rootReducer); // 스토어 생성
// Provider 컴포넌트로 리액트 프로젝트에 리덕스 적용
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root'),
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
5. 컨테이너 컴포넌트 생성
- 컨테이너 컴포넌트와 리덕스를 연동하려면 react-redux에서 제공하는 connect함수를 사용
=> connect( mapStateToProps, mapDispatchToProps)(연동할컴포넌트)- mapStateToProps : 리덕스 스토어 안의 상태를 컴포넌트의 props로 넘겨주기 위한 함수. 현재 스토어 안의 state를 파라미터로 받는다
- mapDispatchToProps : store의 내장함수 dispatch를 파라미터로 받아 이를 이용하여 액션생성함수를 컴포넌트의 props로 넘겨주기 위한 함수. 액션생성함수를 객체형태로 전달만 해도 connect 함수가 자동bindActionCreators 작업을 수행하므로 dispatch를 파라미터로 받지 않아도 된다.
- mapStateToProps 와 mapDispatchToProps 에서 반환하는 객체 내부의 값들은 컴포넌트의 props로 전달된다.
// src/containers/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}
/>
);
};
const mapStateToProps = state => {
return {
input: state.todos.input,
todos: state.todos.todos,
};
};
// dispatch를 파라미터로 받지 않고, 액션생성함수를 객체형태로 전달만 해도
// connect 함수가 자동bindActionCreators 작업을 수행한다.
const mapDispatchToProps = {
changeInput,
insert,
toggle,
remove,
};
export default connect(mapStateToProps, mapDispatchToProps)(TodosContainer);
// src/App.js
import React from 'react';
import TodosContainer from './containers/TodosContainer';
import './App.css';
function App() {
return (
<div>
<TodosContainer />
</div>
);
}
export default App;
// src/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)}>remove</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 (
<>
<form onSubmit={onSubmit}>
<input value={input} onChange={onChange} />
<button type="submit">register</button>
</form>
<div>
{todos.map(todo => (
<TodoItem
todo={todo}
key={todo.id}
onToggle={onToggle}
onRemove={onRemove}
/>
))}
</div>
</>
);
};
export default Todos;
6. redux-actions 라이브러리 적용
- 액션생성함수, 리듀서를 작성할 때 redux-actions 라이브러리 활용하여 리액트 앱에서 리덕스를 훨씬 편하게 사용할 수 있다.
- createAction : 매번 객체를 직접 만들어 줄 필요 없이 간단하게 액션 생성 함수 선언 가능
- handleAction : 첫 번째 파라미터에는 각 액션에 대한 업데이트 함수, 두 번째 파라미터에는 초기 상태 전달.
'Javascript > Redux' 카테고리의 다른 글
#12 Redux (0) | 2020.02.25 |
---|---|
middleware (0) | 2019.09.02 |
Redux Life Cycle (0) | 2019.08.29 |