로그인 여부에 따라 각기 다른 페이지를 연결하는 방법
import React from 'react';
import { Redirect, Route } from 'react-router-dom';
import { useUserState } from '../contexts/UserContext';
function PrivateRoute({ component: Component, ...rest }) {
const { userName } = useUserState();
return <Route {...rest} render={(props) => (userName ? <Redirect to="/category" /> : <Component {...props} />)} />;
}
export default PrivateRoute;
function App() {
return (
<Header />
<PrivateRoute path="/" exact component={Home} />
<Route path="/login" component={Login} />
<Route path="/join" component={Join} />
<Route path="/category" component={Category} />
</AppProvider>
);
}
코드의 가독성을 지키며 Context API를 여러개 사용하는 방법
const AppProvider = ({ contexts, children }) =>
contexts.reduce(
(prev, context) =>
React.createElement(context, {
children: prev,
}),
children
);
function App() {
return (
<AppProvider contexts={[UserProvider, HeaderProvider]}>
<Header />
<PrivateRoute path="/" exact component={Home} />
<Route path="/login" component={Login} />
<Route path="/join" component={Join} />
<Route path="/category" component={Category} />
</AppProvider>
);
}
조건부 라우팅하는 방법
로그인 페이지에서 아이디와 비밀번호가 입력되지 않았거나 입력한 정보와 일치하는 회원이 없는 경우에는 카테고리페이지로 이동하지 못하도록 하려고 함
Link에 onClick 이벤트를 걸어 입력값을 확인하는 함수를 연결해줌
<Link to="/category" onClick={confirmInputs}>
<button>{Words.LOGIN}</button>
</Link>
페이지 이동이 불가능한 경우에는 e.preventDefault()를 사용하여 페이지의 이동을 막는다.
const confirmInputs = (e) => {
inputsDispatch({ type: 'CHANGE_ID', value: !isEmpty(id) });
inputsDispatch({ type: 'CHANGE_ID', value: !isEmpty(password) });
if (!idCorrect || !passwordCorrect) {
e.preventDefault();
}
};
if (isEmpty(id) || isEmpty(password)) {
e.preventDefault();
}
<input name="id" value={id} onChange={onChange} onBlur={함수명}/>
.auth {
position: relative;
text-align: center;
**margin: 0;**
}
.auth-form {
position: relative;
width: 50%;
**margin: 0 auto;**
text-align: center;
background-color: pink;
}
.social-image {
**float: left;**
width: 38px;
height: 38px;
padding-left: 3%;
}

⇒ div의 children 엘리먼트가 두개이고 버튼의 div영역이 두번째 엘리먼트일 경우에 해당되는 방법
.header {
display: flex;
**justify-content: space-between;**
width: 100%;
height: 90px;
bottom: 90%;
}
카테고리 클릭 시 카테고리 state변수를 변경시킴
div태그의 style속성을 사용하여 현재 state변수와 렌더링된 카테고리 이름의 일치 여부에 따라 배경색상을 다르게 설정하도록 한다.
// Category.js
<div
className="category"
onClick={() => {
selectCategory(categoryName);
}}
style={{ background: category === categoryName ? blueGray : 'white' }}
>
.search-input {
width: 77%;
margin-left: 3%;
border: 0px;
**&:focus {
outline: none;
}**
}
<label className="check-label">
<input
className="check-input"
id={post.id}
type="checkbox"
key={getKey(post.id, 'input')}
onClick={(e) => {
console.log(e.target.id);
}}
/>
<span className="check-box"></span>
</label>
.check-label {
display: flex;
align-items: center;
margin-right: 1%;
}
.check-input {
display: none;
}
.check-box {
display: inline-block;
width: 15.5px;
height: 15.5px;
border: 1px solid $blue;
box-sizing: border-box;
cursor: pointer;
background-color: $white;
border-radius: 2px;
}
.check-input:checked ~ .check-box {
background: url(../common/icon/checkIcon.png) $blue no-repeat center/10px;
}
특정 아이템 선택하는 방법
체크박스값이 바뀔때마다 해당 체크박스의 checked여부에 따라 아이템을 checkedItems에 추가하거나 삭제한다.
checked속성은 해당 아이템이 checkedItems에 포함되어 있다면 true, 포함되어 있지않다면 false이다.
⇒ 처음에는 각 Post 컴포넌트에서 생성한 state변수를 사용함. 하지만 이렇게 하면 각 컴포넌트의 체크박스의 state 변수들을 한번에 초기화할 수 없어서 방법 변경함
// Post.js
const { checkedItems } = useCheckedItemsState();
const checkedItemsDispatch = useCheckedItemsDispatch();
const { checkBoxVisibility } = useCheckBoxVisibilityState();
const checkHandler = ({ target }) => {
if (target.checked) {
checkedItemsDispatch({ type: 'ADD_ITEM', item: post.id });
} else {
checkedItemsDispatch({ type: 'DELETE_ITEM', item: post.id });
}
;
...
{checkBoxVisibility && (
<label className="check-label">
<input
className="check-input"
type="checkbox"
onChange={(e) => {
checkHandler(e);
}}
checked={checkedItems.includes(post.id)}
/>
<span className="check-box"></span>
</label>
...
전체 아이템 선택하는 방법
전체선택 체크박스값이 바뀔때마다 checked여부에 따라 각기 다르게 진행한다.
⇒ isAllChecked를 true로 변경하고 현재 렌더링 된 아이템들 중 checkedItems에 포함되지 않은 아이템들만 새로 checkedItems에 추가한다.
⇒ isAllChecked를 false로 변경하고 checkedItems를 초기화한다.
CheckBoxVisibility가 false로 변경되거나 아이템 삭제가 완료된 후에는 isAllChecked를 false로 변경하며 checkedItems를 초기화한다.
// PostList.js
const { checkedItems } = useCheckedItemsState();
const { isAllChecked } = useIsAllCheckedState();
const isAllCheckedDispatch = useIsAllCheckedDispatch();
const checkedItemsDispatch = useCheckedItemsDispatch();
const { checkBoxVisibility } = useCheckBoxVisibilityState();
const checkBoxVisibilityDispatch = useCheckBoxVisibilityDispatch();
const resetCheckedItems = () => {
isAllCheckedDispatch({ type: 'NOT_CHECKED' });
checkedItemsDispatch({ type: 'RESET_ITEM' });
};
const allCheckedHandler = ({ target }) => {
if (target.checked) {
isAllCheckedDispatch({ type: 'CHECKED' });
postList.map((post) => {
if (!checkedItems.includes(post.id)) {
checkedItemsDispatch({ type: 'ADD_ITEM', item: post.id });
}
});
} else {
resetCheckedItems();
}
};
...
{checkBoxVisibility && (
<>
<label className="check-label">
<input
className="check-input"
type="checkbox"
checked={isAllChecked}
onChange={(e) => {
allCheckedHandler(e);
}}
/>
<span className="check-box"></span>
</label>
<span className={classNames('text', 'post-list', 'bold')}>{Words.ALL_CHECK}</span>
</>
)}
...
카테고리가 변경되거나 아이템 삭제가 완료된 후, 또한 삭제버튼이 새로 클릭될때마다 isAllChecked를 false로 변경하고 checkedItems를 초기화해야한다.
객체형태의 배열의 특정 원소들만 문자열로 변환하는 방법
CORS(Cross Origin Resource Sharing)
SOP(Same-Origin Policy)
pagination 구현하는 방법
div의 width를 auto로 지정하면서 가운데에 놓는 방법
axios 이용해서 form-data 전송하기
변수와 헤더 정보 동시에 API로 보내기
postList.js는 총 4번 렌더링 됨
keywords 중복 검사 및 갯수 제한하는 방법
option에서 value 두개 받는 방법
inline-block으로 설정된 div내에서 top, bottom 등 설정하는 방법