리덕스 흐름 분석
기본 : Action creator > action > dispatch > reducer > state
- component 내 이벤트 호출(클릭, 입력 등)
- 이벤트와 연결된 action creator 호출
- action creator가 생성한 action호출
- action이 reducer로 전달 됨(dispatch에 의해)
- dispatch된 action의 영향으로 reducer의 state값이 변화
- 렌더링
노드버드 로그인
- redux 세팅
import { createWrapper } from 'next-redux-wrapper';
import { applyMiddleware, compose, createStore } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import createSagaMiddleware from 'redux-saga';
import reducer from '../reducers';
import rootSaga from '../sagas';
const configureStore = context => {
console.log(context);
const sagaMiddleware = createSagaMiddleware();
const middlewares = [sagaMiddleware];
const enhancer =
process.env.NODE_ENV === 'production'
? compose(applyMiddleware(...middlewares))
: composeWithDevTools(applyMiddleware(...middlewares));
const store = createStore(reducer, enhancer);
store.sagaTask = sagaMiddleware.run(rootSaga);
return store;
};
const wrapper = createWrapper(configureStore, {
debug: process.env.NODE_ENV === 'development',
});
export default wrapper;
import wrapper from '../store/configureStore';
const App = ({ Component }) => (
<>
<Head>
<meta charSet="utf-8" />
<title>NodeBird</title>
</Head>
<Component />
</>
);
App.propTypes = {
Component: PropTypes.elementType.isRequired,
};
export default wrapper.withRedux(App);
- component 내 이벤트 호출
- 이벤트와 연결된 action creator 호출
- action creator가 생성한 action호출
- action이 reducer로 전달 됨(dispatch에 의해)
- 로그인 버튼 클릭 - submit -> onSubmitForm 함수 실행
-> dispatch안의 loginRequestAction에 의해 action이 만들어짐
export const loginRequestAction = data => ({
type: LOG_IN_REQUEST,
data,
});
const LoginForm = () => {
const dispatch = useDispatch();
const { logInLoading } = useSelector(state => state.user);
const [email, onChangeEmail] = useInput('');
const [password, onChangePassword] = useInput('');
const onSubmitForm = useCallback(() => {
console.log(email, password);
dispatch(loginRequestAction({ email, password }));
}, [email, password]);
return (
<FormWrapper onFinish={onSubmitForm}>
<div>
<label htmlFor="user-id">이메일</label>
<br />
<Input
name="user-id"
type="email"
value={email}
onChange={onChangeEmail}
required
/>
</div>
<div>
<label htmlFor="user-password">비밀번호</label>
<br />
<Input
name="user-password"
type="password"
value={password}
onChange={onChangePassword}
required
/>
</div>
<ButtonWrapper>
<Button type="primary" htmlType="submit" loading={logInLoading}>
로그인
</Button>
<Link href="/signup">
<a>
<Button>회원가입</Button>
</a>
</Link>
</ButtonWrapper>
</FormWrapper>
);
};
- (dispatch에 의해) action이 reducer와 saga로 전달 됨
- dispatch된 action의 영향으로 reducer의 state값이 변화
const reducer = (state = initialState, action) => {
switch (action.type) {
case LOG_IN_REQUEST:
return {
...state,
logInLoading: true,
logInError: null,
logInDone: false,
};
case LOG_IN_SUCCESS:
return {
...state,
logInLoading: false,
logInDone: true,
me: dummyUser(action.data),
};
case LOG_IN_FAILURE:
return {
...state,
logInLoading: false,
logInError: action.error,
};
default:
return state;
}
};
import { all, delay, fork, put, takeLatest } from 'redux-saga/effects';
import {
LOG_IN_FAILURE,
LOG_IN_REQUEST,
LOG_IN_SUCCESS,
} from '../reducers/user';
function* login(action) {
try {
console.log('login gen');
yield delay(1000);
yield put({
type: LOG_IN_SUCCESS,
data: action.data,
});
} catch (err) {
yield put({
type: LOG_IN_FAILURE,
error: err.response.data,
});
}
}
function* watchLogin() {
yield takeLatest(LOG_IN_REQUEST, login);
}
export default function* userSaga() {
yield all([fork(watchLogin), fork(watchLogout), fork(watchSignUp)]);
}
- 렌더링
const AppLayout = ({ children }) => {
const { me } = useSelector(state => state.user);
return (
<div>
{children}
{me ? <UserProfile /> : <LoginForm />}
</div>
);
};
AppLayout.propTypes = {
children: PropTypes.node.isRequired,
};
export default AppLayout;