[3.기능개발] Update - 기존 코드 && 코드리뷰

디테일 페이지

수정화면


기존 값 보여주고 변경 저장됨

기존 코드

pages 폴더의 Detail.tsx

Contents컴포넌트와 IsShowEditButton 컴포넌트의 조합

organisms폴더의 Contents.tsx

...
export default function Contents() {
  const contents = useContents();
  const edit = useRecoilValue(editState);

  return edit ? (
    <Box
      component="form"
      mt={2}
      sx={{
        '& .MuiTextField-root': { m: 2, width: '38ch' },
      }}
      noValidate
      autoComplete="off"
    >
      <CreateTextField />
    </Box>
  ) : (
    <Box
      mt={2}
      component="div"
      sx={{ whiteSpace: 'normal', overflow: 'auto', maxHeight: 500 }}
    >
      <Typography variant="h5" component="div">
        {contents?.title}
      </Typography>
      <Typography variant="body1" mt={2}>
        {contents?.contents}
      </Typography>
    </Box>
  );
}

organisms폴더의 CreateTextField.tsx

...
export default function CreateTextField() {
  const contents = useContents();
  const input = useRecoilValue(todoListInput);
  const onChange = useOnChange();

  return contents === null ? (
    <>
      <TextField
        required
        id="outlined-required"
        label="Title"
        name="title"
        value={input.title}
        onChange={onChange}
      />
      <TextField
        required
        id="outlined-multiline-static"
        label="Contents"
        multiline
        rows={4}
        name="contents"
        value={input.contents}
        onChange={onChange}
      />
    </>
  ) : (
    <>
      <TextField
        required
        id="outlined-required"
        label="Title"
        name="title"
        defaultValue={contents.title}
        onChange={onChange}
      />
      <TextField
        required
        id="outlined-multiline-static"
        label="Contents"
        multiline
        rows={4}
        name="contents"
        defaultValue={contents.contents}
        onChange={onChange}
      />
    </>
  );
}

문제점 : defaultValue와 onChange의 value가 맞지 않아서 따로 논다.
해결 : input값에 contents를 넣어주자

organisms폴더의 IsShowEditButton.tsx

import { useRecoilValue } from 'recoil';
import { editState } from 'store';
import EditButton from './EditButton';
import EditFloatingButton from './EditFloatingButton';

export default function IsShowEditButton() {
  const state = useRecoilValue(editState);

  return state ? <EditFloatingButton /> : <EditButton />;
}

EditFloatingButton => 수정한 내용을 넣어주는 Button 컴포넌트

EditButton => editState를 변경하는 Button 컴포넌트

코드리뷰

1. CreateTextField.tsx

A. defaultValue와 onChange는 지양하세요.

defaultValue는 onChange로 인한 변화와 관계가 없기 때문 입니다.

B. input값에 contents를 넣어준다면 삼항연산자가 없어도 됩니다. (1-A의 요구를 같이 해결)

export const useEditState = () => {
  const origin = useContents();

  return useRecoilTransaction_UNSTABLE(
    ({ set }) =>
      () => {
        set(editState, (state) => !state);
        if (origin) {
          set(todoListInput, (s) => ({
            ...s,
            title: origin.title,
            contents: origin.contents,
          }));
        }
      },
    [],
  );
};

B-2. 수정이 완료된 후에는 input값을 초기화로 돌려야하니, ComponentWillUnmount 를 활용하세요.

export const useResetInput = () => {
  const reset = useResetRecoilState(todoListInput);
  useEffect(() => {
    return () => reset();
  }, []);
};

2. 수정하는 Recoil

A. map은 반환하는 함수 입니다. 변수로 담아서 활용하세요.

list.map((listItem) =>
  listItem.id === Number(id)
    ? {
        ...listItem,
        title: input.title,
        contents: input.contents,
      }
    : listItem,
);

이경우, 의도했던 수정이 아니라 새로 생성되버립니다.

따라서 다음과 같이 변수로 담아야 합니다.

const newList = list.map((listItem) =>
  listItem.id === Number(id)
    ? {
        ...listItem,
        title: input.title,
        contents: input.contents,
      }
    : listItem,
);

B. Object.assign()를 잘활용하세요

  • 기존
set(todoListInput, (s) => ({
  title: origin.title,
  contents: origin.contents,
}));

이 경우, 변수 s를 사용하지 않았다는 lint warning이 뜬다.

  • 수정
set(todoListInput, (s) => ({
  ...s,
  title: origin.title,
  contents: origin.contents,
}));

좋은 웹페이지 즐겨찾기