iT邦幫忙

2023 iThome 鐵人賽

DAY 18
0
Modern Web

react 學習記錄系列 第 18

[Day18]我的 react 學習記錄 - Render Props

  • 分享至 

  • xImage
  •  

這篇文章的主要內容

簡單說明如何透過 Render Props 的方法讓父元件決定子元件要顯示的內容。


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 就會看到我的 你好~~~ 就出現在裡面了。

https://ithelp.ithome.com.tw/upload/images/20230922/20161583XQnOP5EoFR.png

當然因為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>
  );
}

可以在子元件裡面傳入一些參數,我在這邊傳入了一個時間,並且在父元件使用這個時間顯示在內容後面。
很多套件會用這個做法把一些變數傳到元件外面。

https://ithelp.ithome.com.tw/upload/images/20230922/20161583Kxc8BMsKnh.png


children

另外除了上面的寫法以外,還有另一個方法,我們的元件目前看的 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


上一篇
[Day17]我的 react 學習記錄 - custom hook
下一篇
[Day19]我的 react 學習記錄 - memo
系列文
react 學習記錄30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言