ReScript in Korean

JSX를 넘어서

원문

JSX는 HTML과 같은 방식으로 리액트 컴포넌트를 사용할 수 있게 해주는 문법적 설탕입니다. 컴포넌트는 특정 인터페이스 규칙을 준수해야합니다. 그렇지 않으면 JSX에서 사용할 수 없습니다. 이 문서에서는 JSX 변환이 작동하는 방식과 그 아래에서 사용되는 리액트 API에 대해 자세히 설명합니다.

참고 이 페이지에서는 React.createElement 또는 ReactDOMRe.createDOMElementVariadic같은 엘리먼트 생성하기 저수준 API에 대한 지식이 필요합니다.

참고 이 페이지는 올바른 JSX 변환을 위해 여러분의 bsconfig.json 설정에 "reason": { "react-jsx": 3 } 로 설정되어있다 가정합니다.

컴포넌트 타입

일반적인 리액트 컴포넌트는 ('props) => React.element 함수로 정의 되어있습니다. 축약 타입인 React.component('props)를 사용하여 컴포넌트를 보다 효율적으로 표현할 수 있습니다.

다음은 컴포넌트 타입을 정의하는 몇가지 예시 입니다. (기존 자바스크립트 코드와 인터롭하거나 컴포넌트를 전달할 때 종종 유용함)

/* 일반적인 함수 타입 */
type friendComp =
({"name": string, "online": bool}) => React.element;
/* 다음 시그니쳐를 가진 함수와 같습니다 */
/* ({"padding": string, "children": React.element}) => React.element */
type containerComp =
React.component({
"padding": string,
"children": React.element
});

위 타입은 매우 낮은 수준(low level)(기본적으로 리액트 컴포넌트 요소의 자바스크립트 표현)이지만 리스크립트 리액트에서는 보다 자체적인 언어 방식으로 리액트 컴포넌트를 정의하는 방법이 있으므로 이러한 구조를 자세히 살펴보겠습니다.

JSX 컴포넌트 인터페이스

리스크립트 리액트 컴포넌트는 JSX에서 사용할 수 있는 makemakeProps 함수가 있는 (하위)-모듈이어야 합니다. 작업을 더 쉽게하기 위해 우리는 이러한 함수를 생성할 수 있는 @react.component 데코레이터를 제공합니다.

module Friend = {
@react.component
let make = (~name: string, ~children) => {
<div>
{React.string(name)}
children
</div>
}
}
module Friend = {
@obj
external makeProps: (
~name: string,
~children: 'children,
~key: string=?,
unit) => {. "name": string, "children": 'children'} = "";
let make = (props: {. "name": string, "children": 'children}) => {
/* 오리지널 make 함수에서 리액트 엘리먼트 생성 */
}
}

확장된 출력

  • makeProps: 여러개의 prop 이름에 따라 명명된 파라미터(labeled arguments)를 받고 make(props)가 소비하는 값을 반환하는 함수
  • make: 컴포넌트 인터페이스 (props) => React.element를 준수하는 변환 된 make 함수

참고 makeProps 함수는 또한 항상 ~key prop 을 포함합니다.

특별 케이스 React.forwardRef

@react.component 데코레이터는 React.forwardRef 호출에도 동작합니다.

module FancyInput = {
@react.component
let make = React.forwardRef((~className=?, ~children, ref_) =>
<div>
/* use ref_ here */
</div>
)
}
/* Simplified Output */
module FancyInput = {
@obj
external makeProps: (
~className: 'className=?,
~children: 'children,
~key: string=?,
~ref: 'ref=?,
unit,
) => {"className": option<'className>, "children": 'children} = ""
let make =
(~className=?, ~children) => ref_ => ReactDOMRe.createDOMElementVariadic("div", [])
let make = React.forwardRef(
(props: {"className": option<'className>, "children": 'children}, ref_,) => {
make(
~className=props["className"],
~children=props["children"],
ref_)
})
}

위 확장된 출력에서 볼 수 있듯, 데코레이터는 일반적인 컴포넌트 make함수와 같은 방식으로 React.forwardRef에 전달된 함수의 문법적 설탕을 제거합니다. 또한 ref props 으로 makeProps함수를 생성하므로 JSX 호출에서 사용할 수 있습니다. (<FancyInput ref=.../>)

이제 리스크립트 리액트 컴포넌트 요소 변환이 어떻게 작동하는지 알았으니 리스크립트가 JSX 구조를 어떻게 변환하는지 살펴보겠습니다.

JSX 자세히 살펴보기

커스텀 컴포넌트("대문자로 시작하는 JSX")와 함께 JSX를 사용할 때마다 실제로는 React.createElement를 사용하여 새 엘리먼트를 만듭니다. 다음은 자식 요소가 없는 리액트 컴포넌트의 예시입니다.

<Friend name="Fred" age=1 />
React.createElement(Friend.make, Friend.makeProps(~name="Fred", ~age=1, ()))

보시다시피 Friend.makeFriend.makeProps를 사용하여 React.createElement API를 호출합니다. 자식 컴포넌트가 있는 경우 React.createElementVariadic을 대신 사용합니다. (React.createElement에 대한 다른 바인딩입니다)

<Container width=200>
{React.string("Hello")}
{React.string("World")}
</Container>
React.createElementVariadic(
Container.make,
Container.makeProps(~width=200, ~children=React.null, ()),
[{React.string("Hello")}, {React.string("World")}],
)

리액트는 세 번째 인수인 자식 컴포넌트 배열만 고려하므로 ~children=React.null prop은 관계가 없습니다.

DOM 엘리먼트

"소문자로 시작하는 JSX" 표현식은 DOM 요소로 취급되며 ReactDOMRe.createDOMElementVariadic 함수 호출로 변환됩니다.

<div title="test"/>
ReactDOMRe.createDOMElementVariadic("div", ~props=ReactDOMRe.domProps(~title="test", ()), [])

children을 포함한 소문자로 시작하는 JSX도 마찬가지입니다.

<div title="test">
<span/>
</div>
ReactDOMRe.createDOMElementVariadic(
"div",
~props=ReactDOMRe.domProps(~title="test", ()),
[ReactDOMRe.createDOMElementVariadic("span", [])],
)