[FDBS] MarkdownEditor적용기 with Next.js

Jay ·2022년 9월 25일
0
post-custom-banner

목표

마크다운 에디터 react-markdowb-editor를
Fiction/Create 및 Edit 페이지에 적용하기
setup Column용.

Problem 1

REFERENCE ERROR - window(혹은 다른 무언가) is not defined

cause

Next.js는 렌더링을 할때 기본적으로 SSR과 CSR 모두 적용한다
react-markdown-editor는 SSR을 지원하지 않음.
SSR을 막아야 함.

solution

모듈을 Dynamic하게 Import 하기.

브라우저에서만 작동하는 라이브러리가 많기 때문인지 , Next.js 에서는
선택적으로 SSR을 Disabled 하게 만들수 있는 기능을 지원하고 있다.

Next.js는 ES2020의 dynamic import 문법을 지원한다. dynamic import를 사용하면 모듈을 빌드 타임이 아닌 런타임에 불러오도록 한다. 이를 통해 번들 파일을 분리하고 퍼포먼스 향상을 기대할 수 있다.

  const NoSsrWysiwyg = dynamic(() => import(`/components/WysiwygEditor`), {
    ssr: false,
  });

Problem 2

Markdown Editor에 Input을 입력할때마다 form focus가 lost 되는 현상 발생.

cause

focus가 Lost 되는것은 해당 컴포넌트가 input 입력시마다 리렌더링 되는 것임.

리렌더링 되는 조건? -> state, props, 부모 컴포넌트 변경(리렌더링), shouldComponentUpdate 에서 true가 반환될떄, forceUpdate가 실행될 때

Next.js이기 때문에 module을 dynamic하게 불러왔는데, 컴포넌트 내부에 코드를 작성하였기 때문에 state가 리렌더링 될때마다 함수가 재실행되었고 form이 초기화 되었던 것이다.

solution

컴포넌트 외부 vs 내부, 변수 및 함수 정의
-> Component 외부 vs 내부
퍼포먼스 측면에서 큰 차이는 없으나,
코드를 외부에 두고 가져다쓰면 컴포넌트가 리렌더링 될때 재생성을 하지 않는다.
따라서 컴포넌트 재사용성을 위해서 컴포넌트 바깥에 코드를 적어야 함.
"Declaring the function outside of scope is foremost for readability and reusability."

~~~~
///적용 전
const MdEditor = () => {
  const [md, setMd] = useState<string | undefined>("# Hello World");
  const MDEditor = dynamic(() => import("@uiw/react-md-editor"), {
  ssr: false,
});
  const handleChange = useCallback((md) => {
    setMd(md);
    console.log(md);
  }, []);

  return (
    <>
      <MDEditor value={md} onChange={handleChange} />
    </>
  );
};

export default MdEditor;
///적용 후
~~~
const MDEditor = dynamic(() => import("@uiw/react-md-editor"), {
  ssr: false,
});

const MdEditor = () => {
  const [md, setMd] = useState<string | undefined>("# Hello World");

  const handleChange = useCallback((md) => {
    setMd(md);
    console.log(md);
  }, []);

  return (
    <>
      <MDEditor value={md} onChange={handleChange} />
    </>
  );
};

export default MdEditor;

Problem 3

비제어 컴포넌트 기반 React-hook-form과
제어 컴포넌트 기반 컴포넌트 병합하기

Why react-hook-form?

react-hook-form을 선택한 이유

1) 렌더링 이슈, 동기화 문제 해결

  • 수백개의 input이 있어도 과도한 렌더링 X
  • 비제어 컴포넌트 방식이지만 데이터는 계속 동기화

2) 여러가지 유틸과 코드 단순화

*state colocation?
컴포넌트의 상태를 서로 관련이 있는 것들끼리만 모아 분리하여 위치시키는 방법

solution

MdEditor를 컴포넌트화하여 parent에서 import하는 방식으로 변경.
-> MdEditor state 변경시 다른 input form(react-hook-form)에서 리렌더링이 일어나지 않음.
-> 자식 컴포넌트에서 부모 컴포넌트로 props 넘기기
-> state 끌어올리기 방식(setter 함수 넘겨주기)으로는 리렌더링을 막을수 없음.
-> useRef를 사용?
-> forwardRef?
-> forwardRef vs 전역상태관리라이브러리?

Problem 4

parent 컴포넌트에서 child 컴포넌트의 state 사용하기

solution

State 끌어올리기
(자식 component 에서 부모 component로 state 넘기기.)

컴포넌트 인수로 함수(setter 함수) 넘기기

///parent
<div>
	<MDEditor md={md} handleChange={handleChange} />
</div>



///child
const MdEditor = ({ md, handleChange }: any) => {

  const onChange = (md: any) => {
    handleChange(md);
  };

  return (
    <>
      <MDEditor value={md} onChange={onChange} />
    </>
  );
};

ETC

고차원 컴포넌트(HOC)에서 Ref 전달하기
state 끌어올리기
제어 VS 비제어 컴포넌트
리액트에서의 데이터 흐름 top-down
단일책임원칙
vs {Component()}

profile
Jay입니다.
post-custom-banner

0개의 댓글