React のレンダープロップについて
レンダープロップとは
レンダープロップの公式の説明は以下です。
“レンダープロップ (render prop)”という用語は、値が関数である props を使って、コンポーネント間でコードを共有するためのテクニックを指します。
https://ja.reactjs.org/docs/render-props.html
Reactではコンポーネントごとに状態(state)と、その状態を更新するための処理を持たせることがあります。
例えば、countという値と、それをincrementする処理をもっているコンポーネントがあるとします。
ボタンを押すと、押した回数だけcountという値がincrementされます。
このcountとcountをincrementする処理を他のコンポーネントでも使いたくなったとき、同じ処理を別のコンポーネント内に書くことになるので、コードの重複が生じます。
これを防ぐのがレンダープロップというテクニックです。
サンプルでみてみる
先ほどの例をもとにひとつコンポーネントを作成します。
import React, {useState} from 'react'
const ClickCounter = () => {
const [count, setCount] = useState(0)
const handleClick = () => {
setCount(prev => prev + 1)
}
return (
<div>
<button onClick={handleClick}>count: {count}</button>
</div>
)
}
export default ClickCounter
ボタンをクリックすると、countを+1しています。
このコンポーネントではボタンを押したときですが、onMouseのときに同じ処理をするコンポーネントが必要になったとします。
以下のようなコンポーネントを作成します。
import React, {useState} from 'react'
const MouseoverCounter = () => {
const [count, setCount] = useState(0)
const handleClick = () => {
setCount(prev => prev + 1)
}
return (
<div>
<div onMouseEnter={handleClick}>count: {count}</div>
</div>
)
}
export default MouseoverCounter
return の処理以外が重複しています。
例えば、今はincrementで+1ですが、すべて+5に変更する必要がでたときに、同じ処理を使っているコンポーネントをすべて修正する必要があり、修正漏れが発生するリスクがあります。
次にこれらをレンダープロップを使って書き換えてみます。
状態とそれを管理するロジックだけをもったコンポーネントを作成する
countとcountを更新する処理だけを持ったコンポーネントを作成します。
import React, {useState} from 'react'
const Counter = props => {
const [count, setCount] = useState(0)
const handleClick = () => {
setCount(prev => prev + 1)
}
return (
<div>
{props.render(count, handleClick)}
</div>
)
}
export default Counter
return の中が変更されています。
このCounterコンポーネントが受け取ったpropsのrenderを実行しており、引数にcountと更新処理を渡しています。
つまり、Counterコンポーネントは何を描画するかについては関与せず、props.renderによって渡された関数を実行することで描画するだけ、となります。
次にこのCounterコンポーネントを実際に使用してみます。
import React from 'react'
import Counter from './Counter'
const App = () => {
return (
<div>
<Counter render={(count, handleIncrement) => <button onClick={handleIncrement}>count: {count}</button>} />
</div>
)
}
export default App
renderには関数が渡されています。
この関数の引数には、先ほどCounterコンポーネントで渡した二つの引数(count とそれを更新する関数)が渡されています。
そして、描画したいbuttonを返しています。
同じようにMouseoverもCounterコンポーネントを使ってみます。
import React from 'react'
import Counter from './Counter'
const App = () => {
return (
<div>
<Counter render={(count, handleIncrement) => <button onClick={handleIncrement}>count: {count}</button>} />
<Counter render={(count, handleIncrement) => <div onMouseEnter={handleIncrement}>count: {count}</div>} />
</div>
)
}
export default App
このようにすることで、状態とその状態を変更する処理を共通化することができます。
レンダープロップはformikなどのライブラリーでも使用されており、使えるようになるとコードの重複がなくなり保守性が上がると思います。
でもカスタムフックの登場によってレンダープロップも下火になるのかな。
https://ja.reactjs.org/docs/hooks-faq.html#do-hooks-replace-render-props-and-higher-order-components
参考
ディスカッション
コメント一覧
まだ、コメントがありません