지금 작성하는 atom이라던가 모든 것 분리 할 것 임.
1. src > components 폴더 생성 > toDoList.tsx 파일 이동
2.
const toDoState = atom({
key:"toDo",
default: [ ] //빈 배열
})
3.
function ToDoList(){
const value = useRecoilValue(toDoList); // 위 아래 각각 atom에 기존 값을 가져오고, 변경하는 함수로 사용 하였는데
const modFn = useSetRecoilState(toDoList) //
}
function ToDoList(){
const [value, modFn]= useRecoilState(toDoList);// const[x, setX] =useState()처럼 사용가능.
}
useRecoilValue: state값을 리턴
useSetRecoilState: setter 함수를 리턴
useRecoilState: state, setter 함수를 모두 리턴
4.
const handleValid = (data: IForm) => {
console.log("add to do ", data.toDo);
setValue("toDo", "");
setToDo(["hello"]) //typescript에서는 ToDos가 항상 빈 배열이여야 하기에 이동작은 허용되지 않음
이를 위해 typescript에게 toDo가 어떻게 생긴지를 알려줄 interface를 하나 만듬.
};
interface IToDo {
text: string;
category: "TO_DO" | "DOING" | "DONE";
}
const toDoState = atom<IToDo[]>({
key: "toDo",
default: [],
});
5.
const handleValid = ({ toDo }: IForm) => {
setToDo((oldToDos) => [{ text: toDo, category: "TO_DO" }, ...oldToDos]);
// (oldToDos => [oldTodos])와 같이 반환하게 되면 [[oldToDos]] 배열 안에 배열을 갖게 됨.
[...oldToDos]처럼 사용하게 된다면 oldToDos 배열 안의 값만 들어가게 된다.
};
6.
화면에 그리기
<ul>
{toDos.map((toDo) => (
<li key={toDo.id}>{toDo.text}</li>
))}
</ul>
분리하기
CreateToDo.tsx 컴포넌트 만들어 form 이동!
ToDo.tsx 컴포넌트 만들어 li 부분 이동!
atoms.tsx 만들어서 atom과 해당 interface 이동!
기본틀은 ToDoList.tsx
분리된 CreateToDo에서
const [toDos, setToDo] = useRecoilState(toDoState); 값을 수정만하면되지 가져올 필요는 없다.!
같은맥락에서 ToDoList에서 가져오기 기능만 필요
에러가났었는데 이유는
const {~~}= useForm<IForm>() useForm에 타입스크립트 참조 안해서
function ToDoList() {
return (
<div>
<h1>To Dos</h1>
<hr />
<CreateToDo />
<ul>
{toDos.map((toDo) => ( <ToDo text={toDo.text} category={toDo.category} id={toDo.id}/>
<ToDo {...toDo}/>
위와 같이 작성하여도 작동된다. toDos 배열의 toDo 원소 하나하나가 ToDo 컴포넌트에 필요한 props와 같은 모양이기 때문이다.
))}
</ul>
</div>
);
}
사용자들이 버튼을 이용해서 toDo의 카테고리를 바꿀 수 있게 하는 기능 추가.
카테고리에 맞게 hide&show
function ToDo({ text, category }: IToDo) {
return (
<li>
<span>{text}</span>
{category !== "DOING" && <button>DOING</button>} //category가 "Doing"이아니면 DOING버튼을 나타냄
{category !== "TO_DO" && <button>TO_DO</button>}
{category !== "DONE" && <button>DONE</button>}
</li>
);
}
https://ko.reactjs.org/docs/conditional-rendering.html
- A가 참이면 B
- A가 거짓이면 A
A || B
- A가 참이면 A
- A가 거짓이면 B
편하게 삼항연산자를 사용하듯, 자주 사용됩니다.
const A = 'abc';
const B = '';
console.log(A && B); // A가 참이여서 B가 return -> 빈 공백이 콘솔에 찍힘
console.log(B && A); // B가 거짓이여서 B가 return -> 빈 공백이 콘솔에 찍힘
방법1 . 함수를 만들어서 새 익명 함수에 호출하고 인자를 넘겨주는 방법
function ToDo({ text, category }: IToDo) {
const onClick = (newCategory: IToDo["category"]) => { //ITODO interface에 있는 category 항목
console.log("i Wanna go to ", newCategory);
};
return (
<li>
<span>{text}</span>
{category !== "DOING" && (
<button onClick={() => onClick("DOING")}>DOING</button>
)}
{category !== "TO_DO" && (
<button onClick={() => onClick("TO_DO")}>TO_DO</button>
)}
{category !== "DONE" && (
<button onClick={() => onClick("DONE")}>DONE</button>
)}
</li>
);
}
방법 2
function ToDo({ text, category }: IToDo) {
const onClick = (event: React.MouseEvent<HTMLButtonElement>) => {
console.log("i Wanna go to ", event.currentTarget.name);
};
return (
<li>
<span>{text}</span>
{category !== "DOING" && (
<button name= "DOING" onClick={onClick}>DOING</button>
)}
{category !== "TO_DO" && (
<button name="TO_DO" onClick={onClick}>TO_DO</button>
)}
{category !== "DONE" && (
<button name="DONE" onClick={onClick}>DONE</button>
)}
</li>
);
}
1.find to do based on id
function ToDo({ text, category, id }: IToDo) {
const setToDos = useSetRecoilState(toDoState);
const onClick = (event: React.MouseEvent<HTMLButtonElement>) => {
const {
currentTarget: { name },
} = event;
setToDos((oldToDos) => {
const targetIndex = oldToDos.findIndex((toDo) => toDo.id === id //prop으로 넘겨진 id); //oldToDos는 배열이니 배열의 function인 인덱스 번호 찾기
console.log(targetIndex); // 0 ,1 등 번호로 찾아줌.
return oldToDos;
});
};
return (
참고
function ToDoList() {
const toDos = useRecoilValue(toDoState);
return (
<div>
<h1>To Dos</h1>
<hr />
<CreateToDo />
<ul>
{toDos.map((toDo) => (
<ToDo key={toDo.id} {...toDo} /> prop으로 text, category id 모두 받음.
))}
</ul>
</div>
);
2.
function ToDo({ text, category, id }: IToDo) {
const setToDos = useSetRecoilState(toDoState);
const onClick = (event: React.MouseEvent<HTMLButtonElement>) => {
const {
currentTarget: { name },
} = event;
setToDos((oldToDos) => {
const targetIndex = oldToDos.findIndex((toDo) => toDo.id === id);
const oldToDo = oldToDos[targetIndex];
const newToDo = { text, id, category: name };
console.log(oldToDo, newToDo);
return oldToDos;
});
};
Array.prototype.findIndex()
findIndex() 메서드는 주어진 판별 함수를 만족하는 배열의 첫 번째 요소에 대한 인덱스를 반환합니다. 만족하는 요소가 없으면 -1을 반환합니다.
인덱스 대신 값을 반환하는 find() 메서드도 참고하세요.
```
const array1 = [5, 12, 8, 130, 44];
const isLargeNumber = (element) => element > 13;
console.log(array1.findIndex(isLargeNumber));
// expected output: 3
```
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex
Array.prototype.findIndex() - JavaScript | MDN
findIndex() 메서드는 주어진 판별 함수를 만족하는 배열의 첫 번째 요소에 대한 인덱스를 반환합니다. 만족하는 요소가 없으면 -1을 반환합니다.
developer.mozilla.org
3.replace the todo in the index targetIndex with newToDo
배열의 원소 교체에 대한 이론
바꾸고 싶은 곳의 인덱스 번호를 찾은 후
인덱스 앞 , 뒤 모든 요소를 알아낸다.
const newArray = [...front "교체할것" ...back ]
function ToDo({ text, category, id }: IToDo) {
const setToDos = useSetRecoilState(toDoState);
const onClick = (event: React.MouseEvent<HTMLButtonElement>) => {
const {
currentTarget: { name },
} = event;
setToDos((oldToDos) => {
const targetIndex = oldToDos.findIndex((toDo) => toDo.id === id);
const oldToDo = oldToDos[targetIndex];
const newToDo = { text, id, category: name as any };//category가 todo,doing ,done중에 하나여야하는데 그냥 string이라고 경고 as any로 상관없다고 신경쓰지말라고 타입스크립트에게 전달.
console.log(oldToDo, newToDo);
return [
...oldToDos.slice(0, targetIndex),
newToDo,
...oldToDos.slice(targetIndex + 1),
];
});
};
상태관리툴을 사용 할 때 Mutate하면 안되고 새로운 변수로 State를 바꿔줘야합니다.
Mutate는 let a = 1; 에서 a = 2 와 같이 a를 직접적으로 변경시키는걸 말합니다.
그래서 완전히 새로운 object나 array를 만들어주고 거기에 요소들을 그대로 입력해준다.
splice => side effect : 원래 array가 변형되고 삭제된 elements를 return 함
slice => no side effect : 원래 array를 복제해 행해지고 결과물을 return 함
react로 저장한 state들은 직접적으로 수정할 수 없기때문에 늘 setter함수를 사용해야 함
그래서 splice 같은 side effectr가 있는 함수를 사용할 때에는 복제해서 사용해야 함
recoil의 Selector
카테고리:TODO, DOING, DONE에 따라 정리를 하고 싶음.
그러나 이것을 위해 TODO ATOM, DOING ATOM ,등을 또 만들생각은 없다.
Selector function은 atom의 OUTPUT을 변형시키는 도구
export const toDoState = atom<IToDo[]>({ //이 ATOM은 그냥 배열을 줄뿐 key: "toDo",
default: [],
});
ATOM의 OUTPUT을 변형시키는건 SELECTOR!
selector은 get을 받는데 인자는 객체를 받고 그안에 get이있음
getfunction이 있어야 atom을 받을 수 있다.
selector은 atom을 보고 있어서 atom이변경된다면 selector의 output도 변경됨.
export const toDoSelector = selector({
key: "toDoSelector",
get: ({ get }) => {
const toDos = get(toDoState)
return toDos.length;
},
});
toDoList.tsx
function ToDoList() {
const toDos = useRecoilValue(toDoState);
const selectorOutput = useRecoilValue(toDoSelector);
console.log(selectorOutput);
return (
<div>
<h1>To Dos</h1>
<hr />
<CreateToDo />
<ul>
{toDos.map((toDo) => (
<ToDo key={toDo.id} {...toDo} />
))}
</ul>
</div>
);
}
export const toDoSelector = selector({
key: "toDoSelector",
get: ({ get }) => {
const toDos = get(toDoState);
return [
toDos.filter((toDo) => toDo.category === "TO_DO"),
toDos.filter((toDo) => toDo.category === "DOING"),
toDos.filter((toDo) => toDo.category === "DONE"),
];
},
});
function ToDoList() {
const [toDo, doing, done] = useRecoilValue(toDoSelector); //위엘 보면 큰 배열안에 3개 배열이있다.
미리 배열을 열고 그 안에 각각에대한 이름을 정의해주었음
return (
<div>
<h1>To Dos</h1>
<hr />
<CreateToDo />
<hr />
<h2>toDo</h2>
<ul>
{toDo.map((toDo) => (
<ToDo key={toDo.id} {...toDo} />
))}
</ul>
<hr />
<h2>Doinig</h2>
<ul>
{doing.map((toDo) => (
<ToDo key={toDo.id} {...toDo} />
))}
</ul>
category에 따라서 새 toDo의 카테고리가 정해졌으면 좋겠음
function CreateToDo() {
const setToDos = useSetRecoilState(toDoState);
const category = useRecoilValue(categoryState); //카테고리 스테이트에서 카테고리 받아오기
const handleValid = ({ toDo }: IForm) => {
setToDos((oldToDos) => [
{ text: toDo, id: Date.now(), category: category },
...oldToDos,
]);
setValue("toDo", "");
};
type은 재사용이 가능하다. 복붙을 안하게 해주는 단순한 문법
type categories = "TO_DO" | "DOING" | "DONE";
export interface IToDo {
text: string;
id: number;
category: categories ;
}
export const categoryState = atom<categories >({
key: "category",
default: "TO_DO",
});
function ToDoList() {
const toDos = useRecoilValue(toDoSelector);
const [category, setCategory] = useRecoilState(categoryState);
const onInput = (event: React.FormEvent<HTMLSelectElement>) => {
setCategory(event.currentTarget.value as any); setCategory할 때 인자로 string인 값을 넘김.
};
타입스크립트가 보기에 OPTION의 VALUE는 그냥 STRING
<select value={category} onInput={onInput}>
<option value="TO_DO">To Do</option>
<option value="DOING">Doing</option>
<option value="DONE">Done</option>
</select>
ATOM.TSX
type categories = "TO_DO" | "DOING" | "DONE";
ATOM에 있는 CATEGORIES와 OPTION의 STRING형태의 VALUE가 같은 것인지 타입스크립트는 알지 못한다.
enum: enumerable 셀수 있는
계속해서 써야하는 값을 저장하는 도구
export enum Categories {
"TO_DO",
"DOING" ,
"DONE",
}
export interface IToDo {
text: string;
id: number;
category: Categories;
}
export const categoryState = atom<Categories>({
key: "category",
default: Categories.TO_DO,
});
toDoList.tsx
return (
<div>
<h1>To Dos</h1>
<hr />
<select value={category} onInput={onInput}>
<option value={Categories.TO_DO}>To Do</option>
<option value={Categories.DOING}>Doing</option> //value="Dooing" 같은 실수를 줄 일 수 있다.
<option value={Categories.DONE}>Done</option>
</select>
toDo.tsx
return (
<li>
<span>{text}</span>
{category !== Categories.DOING && (
<button name={Categories.DOING} onClick={onClick}>
DOING
</button>
)}
{category !== Categories.TO_DO && (
<button name={Categories.TO_DO} onClick={onClick}>
TO_DO
</button>
)}
{category !== Categories.DONE && (
<button name={Categories.DONE} onClick={onClick}>
DONE
</button>
)}
</li>
);
Categoreis의 "TO_DO"의 값은 0, 순차대로 1,2이다.
따라서 위의 {category !== Categories.DOING && 이것은 cateogry와 숫자 2와 같냐고 물어보는 격
console.log(ToDos)하면 category:0이라고 나옴
코드 상에서는 각각 0,1,2일 뿐
enum은 개발자가 코드를 더 쉽게 작성 할 수 있도록 도와준다.
export const categoryState = atom<Categories>({
key: "category",
default: Categories.TO_DO, // 실제 값은 0
});
export enum Categories {
"TO_DO" = "TO_DO", //console.log(toDos)하고 category보면 "TO_DO"라고나옴
"DOING" = "DOING",
"DONE" = "DONE",
}
Enums
열거형은 TypeScript가 제공하는 기능 중 하나입니다.
enum은 열거형으로 이름이 있는 상수들의 집합을 정의할 수 있습니다.
열거형을 사용하면 의도를 문서화 하거나 구분되는 사례 집합을 더 쉽게 만들수 있습니다. TypeScript는 숫자와 문자열-기반 열거형을 제공합니다.
숫자 열거형 (Numeric enums)
enum Direction {
Up = 1,
Down,
Left,
Right,
}
문자열 열거형 (String enums)
enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT",
}
등등..
https://www.typescriptlang.org/ko/docs/handbook/enums.html
정리
TODO를 추가하면 TODOSTATE에 추가된다.
TODO를 보려고 할 때는 SELECTOR를 사용하는데 (STATE를 가져다가 조금 변형해주는 함수 )
SELECTOR에서 값을 얻어오려면 STATE처럼 USERECOILVALUE 사용하면딘다.
SELECOTR는 KEY, GET FUNCTION이 있다. GETFUNCTION은 SELECTOR가 어떤 것을 반환할지 결정하는데 GET FUNCTION 은 인자로 객체를 받는데 이 객체는 또 다른 FUNCTION이 들어있고 이함수를 사용하면 원하는 ATOM을 가져올 수있다.
'코딩 > Today I Learn' 카테고리의 다른 글
React Js Master #10 Ref,CrossBoardMovement (0) | 2022.08.03 |
---|---|
React Js Master #9 selector set, beautiful-dnd (0) | 2022.07.11 |
React Js Master #7 React-hook-form ,register,watch, ,handleSubmit, formState (0) | 2022.05.11 |
React Js Master #6 state management Recoil (0) | 2022.05.09 |
React Js Master #5 Chart,React-ApexCharts,react-helmet,BrowserRouter deploy 할 때 (0) | 2022.05.04 |