BoardList.tsx 게시판 목록 생성, boardId 이동
`npm i react-icons`
app.tsx 에서 props 로 activeboardId 상태값을 전달한다. 어떤 게시판이 선택되었는지!
const [activeBoardId, setActiveBoardId] = useState("board-0");
<BoardList
activeBoardId={activeBoardId}
setActiveBoardId={setActiveBoardId}
/>
BoardList.tsx
게시판을 선택했을때, 어떤 boardId가 클릭이 활성화 되어있는지 확인하고, 해당 게시판 정보가 열리게 하기!
- 전역으로 상태값을 전달하면 타입 에러가 발생하기 때문에, 사용할 BoardList 컴포넌트에 props의 타입을 지정한다
type TBoardListProps = {
activeBoardId: string;
setActiveBoardId: React.Dispatch<React.SetStateAction<string>>;
};
const BoardList: FC<TBoardListProps> = ({
activeBoardId,
setActiveBoardId,
}) => {
//...
`React.Dispatch<React.SetStateAction<string>>;` 함수 타입 지정!
-ui
const { boardArray } = useTypedSelector((state) => state.boards); //hooks로 만든 데이터
const [isFromOpen, setIsFromOpen] = useState(false);
<div>게시판 :</div>
{boardArray.map((board, idx) => (
<div key={board.boardId}>
<div>{board.boardName}</div>
</div>
))}
<div>
{isFromOpen ? (
<SideForm setIsFromOpen={setIsFromOpen} />
) : (
<FiPlusCircle onClick={() => setIsFromOpen(!isFromOpen)} />
)}
</div>
board 데이터 > hooks로 만든 `useTypedSelector` 에서 arr로 가져오고,
id가 일치해서 form이 열린상태인지 판단하는 상태값 생성
map 을 돌면서 이름 가져오는 목록 + 클릭되어서 열린 상태인지 아닌지~ > false 일 경우 react-icons/fi 에서 게시물 추가하는 버튼 활성화
*이때 map 돌때 div 에 키값을 넣어주지 않는다면 콘솔경고가 발생한다!! key 값 필수
clsx로 버튼 활성화 상태 BoardList.css.ts
<div
key={board.boardId}
onClick={() => setActiveBoardId(boardArray[index].boardId)}
className={clsx(
{
[boardItemActive]:
boardArray.findIndex((b) => b.boardId === activeBoardId) ===
index,
},
{
[boardItem]:
boardArray.findIndex((b) => b.boardId === activeBoardId) !==
index,
}
)}
>
onclick으로 함수 `setActiveBoardId`에 맞는 해당 boardArray[idx].boardId를 넣어주면, active한 boardId가 바뀐다.
- 브라우저에서 기본으로 제공하는 css 세팅을 초기화하는 index.css 코드
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
SideForm.tsx 게시판 등록하기
`onBlur` 메소드 : 마으수 포커스 없어지는 경우
`useRef` + `ref.current.focus()` : 버튼을 눌렀을 경우 input 창 안 커서에 바로 포커스 가게하는 경우
import React, { ChangeEvent, FC, useState } from "react";
import { FiCheck } from "react-icons/fi";
type TSideFromProps = {
inputRef: React.RefObject<HTMLInputElement>;
setIsFromOpen: React.Dispatch<React.SetStateAction<boolean>>;
};
const SideForm: FC<TSideFromProps> = ({ setIsFromOpen, inputRef }) => {
const [inputText, setinputText] = useState("");
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
setinputText(e.target.value);
};
const handleOnBlur = () => {
setIsFromOpen(false);
};
return (
<div>
<input
// ref={inputRef}
autoFocus
type="text"
placeholder="새로운 게시판 등록하기"
value={inputText}
onChange={handleChange}
onBlur={handleOnBlur}
/>
<FiCheck />
</div>
);
};
export default SideForm;
setIsFromOpen으로 상태값 가져오고 타입 지정한다.
input 창을 만들고 useState로 inputText 값을 기억하고, setInputText 를 onChange 해서 변화를 체크한다.
onChange 할대 이벤크를 감지하고, htmlinput 요소에서 사용되는 changeevent 이므로 해당 타입 선언한다!
FiCheck의 react-icons/fi 사용
인풋창이 아닌 다른 곳 클릭하면 창 닫히게! `onBlur()` 에서 setInformOpen 을 false로 만드는 함수 handleOnBlur
입력버튼 누르면 바로 인풋창에 포커스! 커서에 초점이 가도록 하는 방법 2개
- useRef
- onclick해서 버튼 열릴때 > 바로 ref.current.focus()를 가게 한데
- null 인데 동작하려고 해서 발생하는 타입에러
- HTMLInputElement 타입 선언 > ? 문법으로 null일경우와 값이 있을경우 구분한다
- setTimeout을 0초로 동작하게 두어서 즉시 포커스 가도록 한다.
- autofocus 항목 : 바로 포커스 가도록 한다.
SideFrom.css.ts , 버튼 누르면 > 게시판 추가됨
크롬 Redux 확장 프로그램 설치
Redux store에 값 업데이트 > Action dispatch
체크를 눌렀을때 실행 `<FiCheck className={icon} onMouseDown={handleClick} />`
import { useTypedDispatch } from "../../../hooks/redux";
import { v4 as uuidv4 } from "uuid";
import { addBoard } from "../../../store/slices/boardsSlice";
import { addLog } from "../../../store/slices/loggerSlice";
//...
const dispatch = useTypedDispatch();
//...
const handleClick = () => {
if (inputText) {
}
};
inputText가 존재할때만 액션 dispatch 동작하도록 if 문으로 감싼다.
각각의 `boardsSlicke.ts` `loggerSlice.ts` 에 add 함수를 추가한다. arr에 push 하는 함수
이전 state, 액션 payload 구조분해할당으로 실제값 타입 넣어준다.
//boardsSlice.ts
type TAddBoardAction = {
board: IBoard;
};
const boardsSlice = createSlice({
name: "boards",
initialState,
reducers: {
addBoard: (state, { payload }: PayloadAction<TAddBoardAction>) => {
state.boardArray.push(payload.board);
},
},
});
export const { addBoard } = boardsSlice.actions;
//loggerSlice.ts
const loggerSlice = createSlice({
name: "logger",
initialState,
reducers: {
addLog: (state, { payload }: PayloadAction<ILogItem>) => {
state.logArray.push(payload);
},
},
});
export const { addLog } = loggerSlice.actions;
push 불변성 신경 안써도 된다 > 내부에서 immer 라이브러리 사용하기 때문이다.
payload로 addBoard, addLog 를 받는다.
import { v4 as uuidv4 } from "uuid";
//...
const handleClick = () => {
if (inputText) {
dispatch(
addBoard({
board: { boardId: uuidv4(), boardName: inputText, lists: [] },
})
);
dispatch(
addLog({
logId: uuidv4(),
logMessage: `게시판 등록 : ${inputText}`,
logAuthor: "User",
logTimestamp: String(Date.now()),
})
);
}
};
boardId는 `uuid`로 유니크를 지켜서 사용한다. `npm i --save-dev @types/uuid`
log관리에서도 게시판이 추가된 거에 대한 정보를 store에 추가해주어야 하기 때문에 같이 진행!
이때 > onClick함수로 등록시키면 onBlur 가 먼저 실행되어서 > 등록되지 않는데
onMousedown 으로 사용한다. : 클릭했을때 먼저 이벤트 발생한다
* 순서 : blur > onMouseDown > onMouseUp > onClick