80일차 - react, recoil

·2024년 4월 5일
post-thumbnail

react

todo 수정 폼 구현

function EditTodoModal({ status, todosState, todo }) {
  const onSubmit = (e) => {
    e.preventDefault();

    const form = e.currentTarget;

    form.content.value = form.content.value.trim();

    if (form.content.value.length == 0) {
      alert('할 일 써');
      form.content.focus();
      return;
    }

    // todosState.addTodo(form.content.value);
    // form.content.value = '';
    // form.content.focus();
  };

  return (
    <>
      <Modal
        open={status.opened}
        onClose={status.close}
        className="tw-flex tw-justify-center tw-items-center">
        <div className="tw-bg-white tw-p-10 tw-rounded-[20px] tw-w-full tw-max-w-lg">
          <form onSubmit={onSubmit} className="tw-flex tw-flex-col tw-gap-2">
            <TextField
              minRows={3}
              maxRows={10}
              multiline
              name="content"
              autoComplete="off"
              variant="outlined"
              label="할 일 써"
              defaultValue={todo?.content}
            />
            <Button variant="contained" className="tw-font-bold" type="submit">
              수정
            </Button>
          </form>
        </div>
      </Modal>
    </>
  );
}

todo 수정 폼 처리

// modify v1
  const modifyTodo = (id, content) => {
    const newTodos = todos.map((todo) => (todo.id != id ? todo : { ...todo, content }));
    setTodos(newTodos);
  };

const modifyTodoByIndex = (index, newContent) => {
    const newTodos = todos.map((todo, _index) =>
      _index != index ? todo : { ...todo, content: newContent },
    );
    setTodos(newTodos);
  };

  // modify v2
  const modifyTodoById = (id, newContent) => {
    const index = findTodoIndexById(id);

    if (index == -1) {
      return null;
    }

    modifyTodoByIndex(index, newContent);
  };

할 일 삭제 구현

const removeTodo = () => {
    if (confirm(`${status.todoId}번 할 일을 삭제하시겠습니까?`) == false) {
      status.close();
      return;
    }

    todosState.removeTodo(status.todoId);
    status.close();
  };

스낵바 알림 추가

<Snackbar open={open} autoHideDuration={4000} onClose={() => setOpen(false)}>
       <Alert variant="filled" severity="sucess">
          게시물 삭제됨
       </Alert>
</Snackbar>

Snackbar -> 컴포넌트화 -> 커스텀 훅

function NoticeSnackbar({ status }) {
  return (
    <>
      <Snackbar
        open={status.opened}
        autoHideDuration={status.autoHideDuration}
        onClose={status.close}>
        <Alert variant={status.variant} severity={status.severity}>
          {status.msg}
        </Alert>
      </Snackbar>
    </>
  );
}

function useNoticeSnackbarStatus() {
  const [opened, setOpened] = React.useState(false);
  const [autoHideDuration, setAutoHideDuration] = React.useState(null);
  const [variant, setVariant] = React.useState(null);
  const [severity, setSeverity] = React.useState(null);
  const [msg, setMsg] = React.useState(null);

  const open = (msg, severity = 'success', autoHideDuration = 1000, variant = 'filled') => {
    setOpened(true);
    setMsg(msg);
    setSeverity(severity);
    setAutoHideDuration(autoHideDuration);
    setVariant(variant);
  };

  const close = () => {
    setOpened(false);
  };

  return {
    opened,
    open,
    close,
    autoHideDuration,
    variant,
    severity,
    msg,
  };
}

recoil, 리액트 상태관리 라이브러리 中 1

리액트 장단점

장점 : 편하다
단점 : 공유 데이터는 상위 컴포넌트에서 정의 되어야 한다.
예) 컴포넌트들

  • A에 있는 애를 D2가 쓰려면 A가 C한테 주고 C가 D2한테 줘야한다.
  • A
  • B
  • C
    • D
    • D2
      결국에는 유지되어야 하는 데이터가 있어야하는데 이를 만들기가 까다롭다1.
  • 해결 하기 위해 redux, mobx, recoil 등등 사용함.

recoilRoot, RecoilState

  • import부터 해준다!
import {
	RecoilRoot,
	atom,
	useRecoilState
} from "https://cdn.skypack.dev/recoil";
  • 데이터를 오래 보존 하고 싶으면 이런 기법을 사용하면 된다!
  • 이 각각의 값들을 보존하려면 키가 같으면 안된다.
  • 보존되지 않는이유는 스위치 후 리 렌더링이 되면서 no안의 값이 보존이 되지 않았다. 결정적인 이유는 지역변수라서 그렇다. 수명이 짧다!

복수의 atom, recoil을 활용해 데이터 전달

const page1NoAtom = atom ({
	key : 'app/page1NoAtom',
	default : 0,
});

function Page1() {
	const [no, setNo] = useRecoilState(page1NoAtom);

	return (
		<>
			<h3>페이지1</h3>
			<ul>
				<li>페이지 1의 숫자 : {no}</li>
				<li>
					<button onClick={() => setNo(no + 1)}>페이지 1의 숫자 증가</button>
				</li>
				<li>
					<button onClick={() => setNo(no - 1)}>페이지 1의 숫자 감소</button>
				</li>
			</ul>
		</>
	);
}

const page2NoAtom = atom ({
	key : 'app/page2NoAtom',
	default : 0,
});

function Page2() {
	const [no, setNo] = useRecoilState(page2NoAtom);

	return (
		<>
			<h3>페이지2</h3>
			<ul>
				<li>페이지 2의 숫자 : {no}</li>
				<li>
					<button onClick={() => setNo(no + 1)}>페이지 2의 숫자 증가</button>
				</li>
				<li>
					<button onClick={() => setNo(0)}>페이지 2 숫자 초기화</button>
				</li>
			</ul>
		</>
	);
}

atomFamily

const pageNoAtomFamily = atomFamily({
	key: "app/pageNoAtomFamily",
	default: (no) => 0
});

function Page1() {
	const [no, setNo] = useRecoilState(pageNoAtomFamily(1));

	return (
		<>
			<h3>페이지1</h3>
			<ul>
				<li>페이지 1의 숫자 : {no}</li>
				<li>
					<button onClick={() => setNo(no + 1)}>증가</button>
				</li>
			</ul>
		</>
	);
}

function Page2() {
	const [no, setNo] = useRecoilState(pageNoAtomFamily(2));

	return (
		<>
			<h3>페이지2</h3>
			<ul>
				<li>페이지 2의 숫자 : {no}</li>
				<li>
					<button onClick={() => setNo(no + 1)}>증가</button>
				</li>
			</ul>
		</>
	);
}

function Page3() {
	const [no, setNo] = useRecoilState(pageNoAtomFamily(2));

	return (
		<>
			<h3>페이지3</h3>
			<ul>
				<li>페이지 3의 숫자 : {no}</li>
				<li>
					<button onClick={() => setNo(no + 1)}>증가</button>
				</li>
			</ul>
		</>
	);
}

function Page4() {
	const [no, setNo] = useRecoilState(pageNoAtomFamily(1));

	return (
		<>
			<h3>페이지4(페이지1의 데이터를 공유 받음)</h3>
			<ul>
				<li>페이지 4의 숫자 : {no}</li>
				<li>
					<button onClick={() => setNo(no + 1)}>증가</button>
				</li>
			</ul>
		</>
	);
}

커스텀 훅 도입, 훅에서 atom 활용, 데이터 공유

훅에서 atomFamily 활용, 각 페이지의 default 값 조절

TODO

  • 오늘 적용해본 리액트 라이브러리 recoil 복습하면서 직접 사용해보기
  • 프로젝트 다중데이터 insert 기능 구현 하기

느낀점

  • 후후.. 확실히 이제 많이 익숙해졌다 리액트.. 별 거 있구만 케케
profile
우당탕탕 연이의 개발일기

0개의 댓글