caasih.net

無測無用

useLess

當應用程式狀態更新時, useState hook 讓我們無視時間,拿到最新的值,好像我們一開始看到的就是最後的值一樣。

換個角度看,也可以說 useState 幫我們把值攤到時間上了。例如可以寫一個 useArray ,把陣列中的值變成一系列更新:

import { useState, useEffect } from 'react';

export default function useArray(xs) {
  const [index, setIndex] = useState(0);

  useEffect(() => {
    setIndex(0);
  }, [xs]);

  useEffect(() => {
    if (index < xs.length - 1) setIndex(index + 1);
  }, [xs, index]);

  return xs[index];
}

let xs =  

這東西很沒用,當你把 xs = [a, b, c] 丟給它,只會看到最後的

useSpace

但既然能把值攤到時間上,能不能反過來把時間上的變化,蒐集起來呢?

可以設計一個 useSpace hook :

import { useState, useEffect } from 'react';
import useFold from './use-fold';

const concat = (acc = [], x) => [...acc, x];

function useSpace(state) {
  return useFold(state, concat, []);
}

export default useSpace;

於是我們又把 變回了 []

useWebSocket

靠 React Hooks 與 event listener 互動時,會遇上:「更新值的 function 得隨著值一起更新,於是得一直 add event listener ,再 remove event listener 」。

有了 useSpace ,我們可以:

function useWebSocket(url) {
  // ...

  const [message, setMessage] = useState();
  const messages = useSpace(message);

  // ...

  const handleMessage = useCallback(({ data }) => {
    setMessage(data);
  }, [setMessage]);

  // ...
}

於是 handleMessage 不用看到整個 messages

ws://echo.websocket.org 通訊看看:

但這個問題完全可以靠傳遞一個 update function 給 setState 解決,無用。

<SpaceTime />

我們還可以做出這樣的 component :

import { useSpace } from '@caasi/hooks'

function SpaceTime({ children }) {
  return useSpace(children)
}

export default SpaceTime;

<SpaceTime /> 展開過去繪製過的 children ,於是這樣寫:

import React from 'react'
import styles from './index.css'

function ColorRect({ data }) {
  return (
    <div
      className={styles.colorRect}
      style={data}
    />
  )
}

export default ColorRect
<SpaceTime>
  <ColorRect data={{ backgroundColor: color }} />
</SpaceTime>

就能達成下面的效果:

點下面的方塊:

但這也可以靠 useState 做到,無用。😂

map

既然我們可以把值攤到時間上再組合回來,就可以把 Array::map 藏起來:

import React, { Children, cloneElement } from 'react'
import { useArray } from '@caasi/hooks';
import SpaceTime from './SpaceTime'

function List({ data, children }) {
  const x = useArray(data)
  return x === undefined
    ? null
    : (
      <div>
        <SpaceTime>
          {cloneElement(Children.only(children), { data: x })}
        </SpaceTime>
      </div>
    )
}

export default List

再一口氣畫完一張圖:

<List data={styleMap}>
  <List>
    <ColorRect />
  </List>
</List>

超無用!❤️

更多無用

  • 因為新版的 useSpace 沒辦法 reset ,所以偷偷使用 undefined 當成分隔符號。
  • 要是連續兩個值完全一樣,會被 useArray 忽略。
  • 這些 custom hooks 很難測試,只好靠 E2E test 工具 cypress 來測。

感謝朋友在閒聊時,提供標題 XD

Creative Commons License