위 세가지를 충족하는 컴포넌트를 디자인하기 위한 노력들이 리액트 패턴이 탄생시켰다.
import React from "react";
import { Counter } from "./Counter";
function Usage() {
const handleChangeCounter = (count) => {
console.log("count", count);
};
return (
<Counter onChange={handleChangeCounter}>
<Counter.Decrement icon="minus" />
<Counter.Label>Counter</Counter.Label>
<Counter.Count max={10} />
<Counter.Increment icon="plus" />
</Counter>
);
}
export { Usage };
Counter에 속한다. 자녀 컴포넌트들에게 states, handler를 공유하기 위해 React.context가 사용된다.
import React, { useState } from "react";
import { Counter } from "./Counter";
function Usage() {
const [count, setCount] = useState(0);
const handleChangeCounter = (newCount) => {
setCount(newCount);
};
return (
<Counter value={count} onChange={handleChangeCounter}>
<Counter.Decrement icon={"minus"} />
<Counter.Label>Counter</Counter.Label>
<Counter.Count max={10} />
<Counter.Increment icon={"plus"} />
</Counter>
);
}
export { Usage };
import React from "react";
import { Counter } from "./Counter";
import { useCounter } from "./useCounter";
function Usage() {
const { count, handleIncrement, handleDecrement } = useCounter(0);
const MAX_COUNT = 10;
const handleClickIncrement = () => {
//Put your custom logic
if (count < MAX_COUNT) {
handleIncrement();
}
};
return (
<>
<Counter value={count}>
<Counter.Decrement
icon={"minus"}
onClick={handleDecrement}
disabled={count === 0}
/>
<Counter.Label>Counter</Counter.Label>
<Counter.Count />
<Counter.Increment
icon={"plus"}
onClick={handleClickIncrement}
disabled={count === MAX_COUNT}
/>
</Counter>
<button onClick={handleClickIncrement} disabled={count === MAX_COUNT}>
Custom increment btn 1
</button>
</>
);
}
export { Usage };

Custom Hook pattern은 컴포넌트를 더 많이 제어 할 수 있지만 로직을 생성해야하므로 컴포넌트를 통합하기는 더 어렵게 된다. Props Getters Pattern은 이를 보완한다. props getters 리스트를 제공한다.getter는 여러 prop들을 리턴하는 함수이고 사용자가 JSX element에 링크하기 자연스럽게 네이밍 된다.import React from "react";
import { Counter } from "./Counter";
import { useCounter } from "./useCounter";
const MAX_COUNT = 10;
function Usage() {
const {
count,
getCounterProps,
getIncrementProps,
getDecrementProps
} = useCounter({
initial: 0,
max: MAX_COUNT
});
const handleBtn1Clicked = () => {
console.log("btn 1 clicked");
};
return (
<>
<Counter {...getCounterProps()}>
<Counter.Decrement icon={"minus"} {...getDecrementProps()} />
<Counter.Label>Counter</Counter.Label>
<Counter.Count />
<Counter.Increment icon={"plus"} {...getIncrementProps()} />
</Counter>
<button {...getIncrementProps({ onClick: handleBtn1Clicked })}>
Custom increment btn 1
</button>
<button {...getIncrementProps({ disabled: count > MAX_COUNT - 2 })}>
Custom increment btn 2
</button>
</>
);
}
export { Usage };

getters에 prop들을 overload시켜 유연하게 로직을 추가 시킬 수 있다.getter가 가진 prop list를 파악해야하고 이 때 가독성이 안좋다.Custom Hook Pattern과 유사하고 사용자가 hook으로 전달하는 reducer를 추가로 정의한다. reducer는 컴포넌트 내부 동작을 전달한다.Custom Hook pattern과 연결했지만 Compound components pattern과도 사용 가능하며 reducer를 메인 컴포넌트에 전달해주면 된다.import React from "react";
import { Counter } from "./Counter";
import { useCounter } from "./useCounter";
const MAX_COUNT = 10;
function Usage() {
const reducer = (state, action) => {
switch (action.type) {
case "decrement":
return {
count: Math.max(0, state.count - 2) //The decrement delta was changed for 2 (Default is 1)
};
default:
return useCounter.reducer(state, action);
}
};
const { count, handleDecrement, handleIncrement } = useCounter(
{ initial: 0, max: 10 },
reducer
);
return (
<>
<Counter value={count}>
<Counter.Decrement icon={"minus"} onClick={handleDecrement} />
<Counter.Label>Counter</Counter.Label>
<Counter.Count />
<Counter.Increment icon={"plus"} onClick={handleIncrement} />
</Counter>
<button onClick={handleIncrement} disabled={count === MAX_COUNT}>
Custom increment btn 1
</button>
</>
);
}
export { Usage };
state reducer를 사용하여 사용자가 컴포넌트 내부 동작을 컴포넌트 바깥에서 제어 할 수 있게 해준다.

plug and play 마인드셋에서 멀어진다. 나는 이것을 중요시하는 편이다.Compound components pattern에 대해 알 수 있어 좋은 공부가 되었다.