yarn add redux react-redux
리덕스란?
- 데이터를 한 곳에 몰아넣고 이곳저곳에서 꺼내볼 수 있게 하는, 상태 관리
State
- 저장하고 있는 상태값(=데이터)
- 딕셔너리 형태({[key]: value}) 형태로 보관
Action
- 상태에 변화가 필요할 때 (가지고 있는 데이터를 변경할 때) 발생하는 것
{type: 'CHANGE_STATE', data: {...}} //type = 이름
ActionCreator
const changeState = (new_data) => { //액션을 리턴한다 return { type: 'CHANGE_STATE', data: new_data, } }
Reducer
- 리덕스에 저장된 상태(=데이터)를 변경하는 함수
- 액션 생성 함수 호출 → 액션을 만들면 → 리듀서가 현재 상태(=데이터)와 액션 객체를 받아서 → 새로운 데이터를 만들고 → 리턴
//기본 상태값을 임의로 지정했음
const initialState = {
name: 'yewon'
}
function reducer(state = initialState, action) {
switch(action.type){
//action의 type마다 케이스문을 작성하면
//액션에 따라 새로운 값을 리턴할 수 있음
case CHANGE_STATE:
return {name: 'garam'};
default:
return false;
}
}
Store
- 프로젝트에 리덕스를 적용하기 위해 만드는 것
- 리듀서, 현재 애플리케이션 상태, 리덕스에서 값을 가져오고 액션을 호출하기 위한 내장 함수 포함
dispatch
리덕스의 특징 3가지
- store은 프로젝트 하나 당 1개만 쓴다.
- store의 state는 오직 action으로만 변경할 수 있다.
- 어떤 요청이 와도 리듀서는 같은 동작을 해야한다.
- 리듀서는 순수한 함수여야 한다.
- 파라미터 외의 값에 의존하지 않는다.
- 이전 상태는 수정하지(=건드리지) 않는다.
- 파라미터가 같으면 항상 같은 값을 반환
- 리듀서는 이전 상태와 액션을 파라미터로 받는다.
리덕스를 통한 리액트 상태 관리
- 여러 컴포넌트가 동일한 상태를 보고 있을 때 유용하게 사용
- 데이터를 관리하는 로직을 컴포넌트에서 빼면, 컴포넌트에서는 뷰만 관리할 수 있음 → 유지보수하기 좋다
상태 관리 흐름도
- 리덕스 store을 component에 연결
- component에서 상태 변화가 필요할 때 action 호출
- reducer을 통해 새로운 상태 값을 만듦
- 새 상태값을 store에 저장
- component는 새로운 상태값을 받아옴 (props를 통해 받아오므로 다시 렌더링됨)
덕스(ducks) 구조
- 보통 리덕스를 사용할 때, action, actionCreator, reducer를 분리해서 작성한다.
- 덕스 구조는 모양새로 묶는 대신 기능으로 묶어 작성한다.
- (ex. 버킷리스트의 action, actionCreator, reducer을 한 파일에 넣는다.)
모듈 만들기
- src 폴더 - redux 폴더 - modules 폴더 안에 bucket.js 만들기
- bucket.js
//Action
const LOAD = 'bucket/LOAD';
const CREATE = 'bucket/CREATE';
//initialState
const initialState = {
list: ['영화관 가기', '매일 책읽기', '수영 배우기'],
};
//Action Creator
export const loadBucket = (bucket) => {
return {type: LOAD, bucket};
}
export const createBucket = (bucket) => {
return {type: CREATE, bucket};
}
//Reducer
export default function reducer(state = initialState, action = {}) {
switch (action.type) {
case 'bucket/LOAD':
return state;
case 'bucket/CREATE':
const new_bucket_list = [...state.list, action.bucket];
return {list: new_bucket_list};
default:
return state;
}
}
- src 폴더 - redux 폴더 - configStore.js 만들기
- configStore.js
import {createStore, combineReducers} from 'redux';
import bucket from './modules/bucket';
import {createBrowserHistory} from 'history';
//브라우저 히스토리
export const history = createBrowserHistory();
//root 리듀서
const rootReducer = combineReducers({bucket});
//스토어
const store = createStore(rootReducer);
export default store;
리덕스와 컴포넌트 연결하기
- 스토어를 불러오고 버킷리스트에 주입한다
- index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import {BrowserRouter} from 'react-router-dom';
//버킷리스트에 리덕스를 주입해줄 프로바이더
import {Provider} from 'react-redux';
//연결할 스토어
import store from './redux/configStore';
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>,
document.getElementById('root')
);
컴포넌트에서 리덕스 데이터 사용하기
1. 클래스형 컴포넌트에서 리덕스 데이터 사용하기
- 리덕스 모듈과 connect 함수 불러오기
- 상태값을 가져오는 함수(mapStateToProps)와 액션 생성 함수를 부르는 함수(mapDispatchToProps) 만들기
- connect로 컴포넌트와 스토어 엮기
- this.state에 있는 list를 지우고 스토어에 있는 값으로 바꾸기
- setState를 this.props.create로 바꾸기
- App.js
import React from 'react';
import {withRouter} from 'react-router';
import {Route, Switch} from 'react-router-dom';
//리덕스 스토어와 연결하기 위해 connect 호출
import {connect} from 'react-redux';
//리덕스 모듈에서 액션 생성 함수 가져오기
import {loadBucket, createBucket} from './redux/modules/bucket';
//스토어가 가진 상태값을 props로 받아오기 위해
const mapStateToProps = (state) => ({
bucket_list: state.bucket.list,
});
//액션 생성 함수를 props로 받아오기 위한 함수
const mapDispatchToProps = (dispatch) => ({
load: () => {
dispatch(loadBucket());
},
create: (new_item) => {
dispatch(createBucket(new_item));
}
});
class App extends React.Component {
constructor(props){
super(props);
this.state = {
};
this.text = React.createRef();
}
componentDidMount(){
console.log(this.props);
}
addBucketList = () => {
const new_item = this.text.current.value;
this.props.create(new_item);
};
render(){
return(
<div className="App">
<Container>
<Title>내 버킷리스트</Title>
<Line/>
<Switch>
<Route path="/" exact
render={(props) => (
<BucketList
list={this.props.bucket_list}
history={this.props.history}
/>
)}
/>
<Route path="/detail" component={Detail}/>
<Route
render={(props) => <NotFound history={this.props.history} />}
/>
</Switch>
</Container>
<Input>
<input type='text' ref={this.text}/>
<button onClick={this.addBucketList}>추가하기</button>
</Input>
</div>
);
}
}
//connect로 묶기
export default connect(mapStateToProps, mapDispatchProps)(withRouter(App));
2. 함수형 컴포넌트에서 리덕스 데이터 사용하기
- 훅을 사용해서 액션 생성 함수를 부르고 스토어에 저장된 값을 가져온다
- BucketList.js에 useSelector() 적용
import React from 'react';
//reudx hook 불러오기
import {useDispatch, useSelector} from 'react-redux';
const BucketList = (props) => {
const bucket_list = useSelector(state => state.bucket.list);
return (
<ListStyle>
{bucket_list.map((list, index) => {
return (
<ItemStyle
className='list_item'
key={index}
onClick={() => {
props.history.push('/detail');
}}
>
{list}
</ItemStyle>
);
})}
</ListStyle>
);
};
export default BucketList;
3. 상세페이지에서 버킷리스트 내용 띄우기
import React from 'react';
//redux hook 불러오기
import {useDispatch, useSelector} from 'react-redux';
const Detail = (props) => {
//스토어에서 상태값 가져오기
const bucket_list = useSelector((state) => state.bucket.list);
//url 파라미터에서 인덱스 가져오기
let bucket_list = parseInt(props.match.params.index);
return <h1>{bucket_list[bucket_index]}</h1>;
};
export default Detail;