簡體   English   中英

Next.js,使用隨機屬性而不觸發“服務器/客戶端不匹配”錯誤

[英]Next.js, using random properties without triggering "did not match on server/client" error

我想隨機生成表單輸入的id屬性,以防止它們與具有相同id的其他輸入發生潛在沖突。 如果我在同一頁面上有兩個登錄表單,每個表單都有一個email字段,則可能會發生這種情況。 我想要/需要設置id屬性的原因是我可以在與該輸入對應的label上設置for屬性。 問題是這個隨機生成的id在服務端和客戶端是不一樣的,所以next.js會報錯。 這是一些代碼:

function uniqueId() {
    let first = (Math.random() * 46656) | 0
    let second = (Math.random() * 46656) | 0
    first = ('000' + first.toString(36)).slice(-3)
    second = ('000' + second.toString(36)).slice(-3)
    return first + second
}

const Login = () => {
    const [emailId] = useState(uniqueId())

    return (
        <form>
            <label htmlFor={emailId}>Email</label>
            <input id={emailId} name='email' type='email' />
        </form>
    )
}

這是我得到的錯誤:

警告:道具 'htmlFor' 不匹配。 服務器:“email-txdmls” 客戶端:“email-htte8e”

知道如何生成在服務器/客戶端上一致的隨機 id 嗎? 或者可能是沒有隨機ID的不同方式?

我找到了解決方法。 我不確定這是否是一個很好的解決方案(請參閱下面的解釋)。 只是為了抑制警告消息似乎很麻煩。 仍然很好奇聽到替代解決方案。 老實說,即使是告訴 next.js 忽略差異而不發出警告的方法也可以正常工作(SSR 和客戶端上的 id 不同並不重要)。

所以我所做的是在useEffect鈎子中生成 id。 問題是初始服務器端呈現的 HTML 在輸入上沒有id 直到所有的 JS 都處理完才得到一個 id。 不理想。

const Login = () => {
    const [emailId, setEmailId] = useState(null)

    useEffect(() => {
        setEmailId(uniqueId())
    }, [])

    return (
        <form>
            <label htmlFor={emailId}>Email</label>
            <input id={emailId} name='email' type='email' />
        </form>
    )
}

應該注意的是,第一次渲染時 id 將為null 在此示例中,這不是問題,因為目的主要是將標簽與輸入相關聯,這將在第二次渲染時足夠快地發生。 但是,如果您在其他情況下使用這個想法,請記住它。

如果您想將其封裝到自定義鈎子中,並稍微清理一下您的組件:

const useUniqueId = () => {
    const [id, setId] = useState(null)

    useEffect(() => {
        setId(uniqueId())
    }, [])

    return id
}

const Login = () => {
    const emailId = useUniqueId()
    const nameId = useUniqueId()

    return (
        <form>
            <label htmlFor={nameId}>Name</label>
            <input id={nameId} name='name' type='text' />

            <label htmlFor={emailId}>Email</label>
            <input id={emailId} name='email' type='email' />
        </form>
    )
}

我的解決方案是使用種子隨機數生成器而不是Math.random() 由於我在前端和后端都使用相同的種子,因此它們最終都會獲得相同的 ID。

// https://stackoverflow.com/a/47593316/2405595
function createRandomSeedGenerator(str) {
  let h = 1779033703 ^ str.length;
  for (let i = 0; i < str.length; i++) {
    h = Math.imul(h ^ str.charCodeAt(i), 3432918353);
    h = (h << 13) | (h >>> 19);
  }

  return () => {
    h = Math.imul(h ^ (h >>> 16), 2246822507);
    h = Math.imul(h ^ (h >>> 13), 3266489909);
    return (h ^= h >>> 16) >>> 0;
  };
}

// https://stackoverflow.com/a/47593316/2405595
function createDeterministicRandom(seedString) {
  const generateSeed = createRandomSeedGenerator(seedString);
  let a = generateSeed();
  let b = generateSeed();
  let c = generateSeed();
  let d = generateSeed();

  return () => {
    a >>>= 0;
    b >>>= 0;
    c >>>= 0;
    d >>>= 0;
    var t = (a + b) | 0;
    a = b ^ (b >>> 9);
    b = (c + (c << 3)) | 0;
    c = (c << 21) | (c >>> 11);
    d = (d + 1) | 0;
    t = (t + d) | 0;
    c = (c + t) | 0;
    return (t >>> 0) / 4294967296;
  };
}


const deterministicRandomNumber = createDeterministicRandom(process.env.NODE_ENV);

function uniqueId() {
    let first = (deterministicRandomNumber() * 46656) | 0
    let second = (deterministicRandomNumber() * 46656) | 0
    first = ('000' + first.toString(36)).slice(-3)
    second = ('000' + second.toString(36)).slice(-3)
    return first + second
}

當然,如果出於安全目的需要隨機數,則不應該這樣做。

getServerSideProps() 可以工作。

https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props#using-getserversideprops-to-fetch-data-at-request-time

它可以在服務器中執行一些邏輯,然后將其傳遞給客戶端。 所以你可以讓一個隨機ID在服務器和客戶端上保持一致。

function Page({ randNum }) {
  return <div>{randNum}</div>;
}

// This gets called on every request
export async function getServerSideProps() {
  // Get random number
  randNum = Math.floor(Math.random() * 1000);

  // Pass data to the page via props
  return { props: { randNum } };
}

export default Page;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM