React(14) - functional component + useRef


Posted by TempuraEngineer on 2023-03-09

目錄


useRef是甚麼

useRef是一個hook,其current屬性可以存任何值(mutable),且不會觸發re-render

用來存DOM element的ref

The useRef() Hook isn’t just for DOM refs. The “ref” object is a generic container whose current property is mutable and can hold any value, similar to an instance property on a class.


用法 - 存DOM

這邊舉存DOM的情況為例

假設有兩個組件FileUpload和ImageCrop,它們的關係是平行的

有個2需求

1.FileInput的上傳區域UI要自己刻,不要用原生的input
2.在ImageCrop內的clear button被按時要清空FileUpload的input的值

也就是說FileUpload和ImageCrop都會操作到input

這有三個方式可以處理

  1. 直接把FileUpload和ImageUpload塞成一個組件
  2. render props的方式把回傳ImageUpload的函式傳入FileUpload
  3. 把const inputRef = useRef(null)拉到這兩個組件的父組件裡,然後把inputRef用props

因為1不利於復用,3的話把inputRef放在父組件不太合理,所以以下使用第2種方式

// FileUpload

interface FileUploadProps {
  renderChildren: (inputEle: HTMLInputElement) => ReactNode;
  onFileSelect: (e: ChangeEvent<HTMLInputElement>) => void;
}

const FileUpload: FunctionComponent<FileUploadProps> = ({
  renderChildren,
  onFileSelect
}) => {
  const inputRef = useRef<HTMLInputElement | null>(null);

  return (
    <>
      <div
        style={{
          display: "flex",
          justifyContent: "space-around",
          alignItems: "center",
          borderRadius: 4,
          border: "1px dashed #cccccc",
          padding: 12
        }}
      >
        <h3>select photo you want to upload</h3>
        <button
          onClick={() => {
            inputRef.current?.click();
          }}
        >
          select
        </button>
      </div>
      <input
        type="file"
        ref={inputRef}
        onChange={onFileSelect}
        style={{
          display: "none"
        }}
      />

      <p>{inputRef.current !== null}</p>

      <div style={{ margin: "16px" }}>{renderChildren(inputRef.current)}</div>
    </>
  );
};


// ImageCrop

interface ImageCropProps {
  handlePhotoClear: () => void;
  previewSrc: string;
}

const ImageCrop: FunctionComponent<ImageCropProps> = ({
  handlePhotoClear,
  previewSrc
}) => {
  return (
    <div>
      <img src={previewSrc} alt="" style={{ width: "100%" }} />

      {previewSrc && (
        <>
          <button
            type="button"
            onClick={() => {
              handlePhotoClear();
            }}
          >
            clear
          </button>

          <button type="button" style={{ marginLeft: 16 }}>
            ok
          </button>
        </>
      )}
    </div>
  );
};

用起來會長這樣

<ImageCrop
    previewSrc={previewSrc}
    handlePhotoClear={() => {
        setPreviewSrc("");
        inputEle.value = "";
    }}
/>


用法 - 存值

useRef的另一個用法useState類似,但它可以確保S值的reference永遠是同一個

另外current被更新時,組件也不會重新渲染組件

codesandbox - useRef中使用了useState與useRef

你會發現只有useState的set function被呼叫時顏色才會變

而顏色變是因為每次re-render class name就會改變


參考資料

React doc beta - useRef
【Day.15】React入門 - 非控制組件與useRef


#React #useRef







Related Posts

JavaScript 繼承機制

JavaScript 繼承機制

【JS 專案 - 01】 今晚來點 TodoList...

【JS 專案 - 01】 今晚來點 TodoList...

CSS - Flexbox實現footer置底

CSS - Flexbox實現footer置底


Comments