• 리덕스 패키지 설치하기
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가지

  1. store은 프로젝트 하나 당 1개만 쓴다.
  2. store의 state는 오직 action으로만 변경할 수 있다.
  3. 어떤 요청이 와도 리듀서는 같은 동작을 해야한다.
  • 리듀서는 순수한 함수여야 한다.
    • 파라미터 외의 값에 의존하지 않는다.
    • 이전 상태는 수정하지(=건드리지) 않는다.
    • 파라미터가 같으면 항상 같은 값을 반환
    • 리듀서는 이전 상태와 액션을 파라미터로 받는다.

리덕스를 통한 리액트 상태 관리

  • 여러 컴포넌트가 동일한 상태를 보고 있을 때 유용하게 사용
  • 데이터를 관리하는 로직을 컴포넌트에서 빼면, 컴포넌트에서는 뷰만 관리할 수 있음 → 유지보수하기 좋다

상태 관리 흐름도

  1. 리덕스 store을 component에 연결
  2. component에서 상태 변화가 필요할 때 action 호출
  3. reducer을 통해 새로운 상태 값을 만듦
  4. 새 상태값을 store에 저장
  5. 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. 상세페이지에서 버킷리스트 내용 띄우기

  • Detail.js
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;

+ Recent posts