簡單說明如何透過 Render Props 的方法讓父元件決定子元件要顯示的內容。
目前為只都分享了如何把畫面拆成一個個的元件,但是好像都沒有提到,如果希望在父元件決定子元件要 render 的內容時應該怎麼處理,今天就來分享如何在父層元件決定子層元件要顯示什麼內容。
用上一篇 useClick outside 用到的內容,假設我希望可以在父元件決定 dialog 裡面要顯示什麼內容時該怎麼處理呢?
function Dialog({ close }: { close: () => void }) {
const ref = useClickOutSide(close);
return (
<div className="dialog">
<div ref={ref}>
<p>Hello!!!!</p>
<button onClick={close}>Close</button>
</div>
</div>
);
}
function App() {
const [open, setOpen] = useState(false);
const handleOpenDialog = () => setOpen(true);
const handleCloseDialog = () => setOpen(false);
return (
<div>
<button onClick={handleOpenDialog}>Open</button>
{open && <Dialog close={handleCloseDialog} />}
</div>
);
}
在 javaScript 裡,function 被當作是一等公民,function 可以當作參數被傳遞,當然在 react 當中也可以當作 props 來傳遞。
像這樣就可以在父層決定子層應該顯示什麼內容。
function Dialog({
close,
renderContent,
}: {
close: () => void;
renderContent: () => ReactElement;
}) {
const ref = useClickOutSide(close);
return (
<div className="dialog">
<div ref={ref}>
{renderContent()}
<button onClick={close}>Close</button>
</div>
</div>
);
}
function App() {
const [open, setOpen] = useState(false);
const handleOpenDialog = () => setOpen(true);
const handleCloseDialog = () => setOpen(false);
return (
<div>
<button onClick={handleOpenDialog}>Open</button>
{open && (
<Dialog
close={handleCloseDialog}
renderContent={() => <p>你好~~~</p>}
/>
)}
</div>
);
}
我在 App 的地方多傳入了一個 renderContent
的參數,是一個 function 並且回傳 JSX 的內容,而且在 Dialog 裡面在本來是 hello!!!!! 的地方呼叫這個 renderContent
的 function。
打開 dialog 就會看到我的 你好~~~ 就出現在裡面了。
當然因為renderContent
是一個 function,所以也可以這樣處理。
function Dialog({
close,
renderContent,
}: {
close: () => void;
renderContent: (time: Date) => ReactElement;
}) {
const ref = useClickOutSide(close);
return (
<div className="dialog">
<div ref={ref}>
{renderContent(new Date())}
<button onClick={close}>Close</button>
</div>
</div>
);
}
function App() {
const [open, setOpen] = useState(false);
const handleOpenDialog = () => setOpen(true);
const handleCloseDialog = () => setOpen(false);
return (
<div>
<button onClick={handleOpenDialog}>Open</button>
{open && (
<Dialog
close={handleCloseDialog}
renderContent={(time) => <p>你好~~~~~{time.getFullYear()}</p>}
/>
)}
</div>
);
}
可以在子元件裡面傳入一些參數,我在這邊傳入了一個時間,並且在父元件使用這個時間顯示在內容後面。
很多套件會用這個做法把一些變數傳到元件外面。
另外除了上面的寫法以外,還有另一個方法,我們的元件目前看的 React 元件都是長這個樣子 <Dialog />
都是 self closing 的形式跟普通的 <div></div>
的元件不同,React 的元件也可以寫成 <Dialog></Dialog>
的形式,而元件中間包住的內容一樣會以 props 的方式傳下去,然後我們就可以在子元件裡面使用,傳下去的 props 可以用 children 來取得。
function Dialog({
close,
children,
}: {
close: () => void;
children: ReactElement;
}) {
const ref = useClickOutSide(close);
return (
<div className="dialog">
<div ref={ref}>
{children}
<button onClick={close}>Close</button>
</div>
</div>
);
}
function App() {
const [open, setOpen] = useState(false);
const handleOpenDialog = () => setOpen(true);
const handleCloseDialog = () => setOpen(false);
return (
<div>
<button onClick={handleOpenDialog}>Open</button>
{open && (
<Dialog close={handleCloseDialog}>
<p>你好~~~~~</p>
</Dialog>
)}
</div>
);
}
上面的結果會跟最上面我們呼叫 renderContent
來顯示畫面的的結果相同,但是這樣的可讀性更好了。
那如果我們希望父元件也可以收到子元件的變數時該怎麼辦?
那就把 function 當作 children 傳下去就好了。
function Dialog({
close,
children,
}: {
close: () => void;
children: (time: Date) => ReactElement;
}) {
const ref = useClickOutSide(close);
return (
<div className="dialog">
<div ref={ref}>
{children(new Date())}
<button onClick={close}>Close</button>
</div>
</div>
);
}
function App() {
const [open, setOpen] = useState(false);
const handleOpenDialog = () => setOpen(true);
const handleCloseDialog = () => setOpen(false);
return (
<div>
<button onClick={handleOpenDialog}>Open</button>
{open && (
<Dialog close={handleCloseDialog}>
{(time) => <p>你好~~~~~{time.getFullYear()}</p>}
</Dialog>
)}
</div>
);
}
這樣時間就會出現在我們傳入的內容後面了。
Passing data with a render prop - react document
Children - react document
下一篇簡單介紹優化 react 的其中一個方法 - memo
如果內容有誤再麻煩大家指教,我會盡快修改。
這個系列的文章會同步更新在我個人的 Medium,歡迎大家來看看 👋👋👋
Medium