5주차 과제에서 useReducer로 상태 변경을 시도했다가 객체 속성에 직접 접근하게 되었고, Recoil도 적용하는데 실패했다.
redux 사용법을 익히기 위해 5주차 과제에 적용하다보니 이러한 문제점들을 해결하게 되었다.
$ npm install redux react-redux
src/reducers/loginReducer.js
// action type
const LOGIN = "LOGINREDUCER/LOGIN";
const LOGOUT = "LOGINREDUCER/LOGOUT";
// action creators
const login = userName => ({ type: LOGIN, userName});
const logout = () => ({ type: LOGOUT, userName:""});
action에 따라 reducer를 실행하여 상태를 변경한다.
src/reducers/loginReducer.js
const visitor = {
isLogined: false,
userName: ""
}
function loginReducer(state = visitor, action) {
// 초기 상태로 visitor 지정
switch (action.type) {
case LOGIN:
return {
...state,
isLogined: true,
userName: action.userName
};
case LOGOUT:
return {
isLogined: false,
userName: ""
}
default:
return state;
}
}
export { loginReducer, login, LOGIN, logout, LOGOUT }
각 컨포넌트별로 리듀서를 작성하거나, 또는 프로젝트 마다 특정 조건에 따라 분리하여 리듀서를 작성한다. 그 후 combineReducers()
로 리듀서를 통합한다.
src/reducers/index.js
import { combineReducers } from "redux";
import { loginReducer } from './loginReducer';
const rootReducer = combineReducers({
loginReducer
});
export default rootReducer;
$ npm i -S redux-devtools-extension
createStore
로 store 생성
index.js
// app에 store 넣고, 만든 reducer 반영
import { createStore, applyMiddleware, compose } from "redux";
import { Provider } from "react-redux";
import logger from "redux-logger";
import { composeWithDevTools } from "redux-devtools-extension";
import rootReducer from './reducers';
// 배포 레벨에서는 리덕스 발동시 찍히는 logger를 사용하지 않습니다.
const enhancer =
process.env.NODE_ENV === "production"
? compose(applyMiddleware())
: composeWithDevTools(applyMiddleware(logger));
// 위에서 만든 reducer를 스토어 만들때 넣어줍니다
const store = createStore(rootReducer, enhancer);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
src/Components/LoginForm.js
// redux 사용하기
import { useSelector, useDispatch } from "react-redux";
import { login, logout } from '../reducers/loginReducer';
import { useState, useEffect } from 'react';
import { useSetRecoilState } from 'recoil';
import CommentsForm from './CommentsForm';
import { userNameState } from '../State/userNameState';
import styles from './LoginForm.module.css';
function LoginForm() {
const [input, setInput] = useState("");
// redux 사용하기
// dispatch를 사용하기 위한 준비
const dispatch = useDispatch();
// store에 접근하여 state 가져오기
const { userName } = useSelector(state => state.loginReducer);
const { isLogined } = useSelector(state => state.loginReducer);
// reselect 적용해보기.
const loginFunc = () => {
// store에 있는 state 바꾸는 함수 실행
dispatch(login(input));
}
const logoutFunc = () => {
dispatch(logout());
}
const setUserName = useSetRecoilState(userNameState);
const loginText = isLogined ? "LOGOUT" : "LOGIN";
const getUserName = JSON.parse(window.localStorage.getItem("user-name"));
function onChangeInputHandler(e) {
const text = e.target.value;
setInput(text);
}
function onClickSubmitHandler(e) {
e.preventDefault();
if (!isLogined){
window.localStorage.setItem("user-name", JSON.stringify(input));
loginFunc();
setUserName(getUserName);
return;
}
localStorage.removeItem("user-name");
logoutFunc();
}
const inputText = <input type="text" onChange={onChangeInputHandler}/>;
return (
<div>
<form>
{isLogined ? <h2>{userName}</h2> : inputText}
<button
type="button"
onClick={onClickSubmitHandler}>
{loginText}
</button>
</form>
<CommentsForm isLogined={isLogined} userName={userName}/>
</div>
)
}
export default LoginForm;