Next.js: Q:クライアント側でのみコードを実行する方法

作成日 2017年07月05日  ·  19コメント  ·  ソース: vercel/next.js

アプリケーションに、URLハッシュフラグメントの認証プロセスからアクセストークンを受け取るコールバックルートがあります。 解析してreduxストアに保存したいのですが。 次へ切り替える前にwindow.location.hashを通過していましたが、ページがサーバー側でレンダリングされる場合は使用できません(ハッシュフラグメントもサーバーに送信されないため、そこで解析できません) )。

一部のコードをクライアント側でのみ実行するように制限する方法はありますか? または、これを達成する他の方法はありますか?

最も参考になるコメント

@vannieweltいつでもprocess.browser見ることができます..

if (process.browser) {
  // client-side-only code
}

@Timerによる編集:

@vannieweltいつでもtypeof window見ることができます..

if (typeof window !== 'undefined') {
  // client-side-only code
}

全てのコメント19件

@vanniewelt :動的にインポートされた非同期コンポーネントがクライアントに読み込まれ、すべてのクライアント側ロジックをそこに配置できます。

また、ComponentWillReceivePropsライフサイクルは、サーバー側にいる間、小道具でリクエストを受け取ります。
この変数が存在するかどうかを確認し、クライアントコードを挿入できます。

libにウィンドウ変数が必要な場合は、動的インポートを使用することをお勧めします。

https://github.com/zeit/next.js/tree/v3-beta/examples/with-dynamic-import

ReactのcomponentDidMountライフサイクルメソッドを使用することもできます。 サーバーサイドとは呼ばれません。

@vannieweltいつでもprocess.browser見ることができます..

if (process.browser) {
  // client-side-only code
}

@Timerによる編集:

@vannieweltいつでもtypeof window見ることができます..

if (typeof window !== 'undefined') {
  // client-side-only code
}

はい、しかしそれは最善のアプローチではありません。
ユーザーが認証されているかどうかを確認したいとします。その確認をクライアント側でのみ実行したいのは、いくつかの理由があります。その1つは、JWTをlocalStorageに保存していて、Cookieに保存したくないためです。
したがって、この場合、最初のページで次のようにロードします。

if (process.browser) {
  // client-side-only code
}

スコープ内のコードは、クライアント上ではなくサーバー上ではなく、まったく実行されません。クライアントからルート変更がトリガーされた場合にのみ実行されます。

@sarkistltそのコードが配置されている場所によっては、正しい場合もあります。

そのコードがサーバー上で実行された場合、実行は// client-side-only code到達しません。 そのコードがクライアントで実行されると、実行// client-side-only code達します。

それは質問に対する有効な答えのようです:

一部のコードをクライアント側でのみ実行するように制限する方法はありますか?

@sarkistlt componentDidMountはクライアント側でのみ実行され、 componentWillMountはクライアント側とサーバー側の両方で実行されます。

@sarkistlt https://reactjs.org/docs/react-component.html#componentwillmount

これは、サーバーレンダリングで呼び出される唯一のライフサイクルフックです。

私は何度もそれをしました、あなたはcomponentWillMountcomponentDidMountと混同しています。

@sergiodxaは実際にあなたが正しい、ちょうどテストを実行しました。 ありがとう!

@sergiodxa componentWillMountのリンクがhttps://reactjs.org/docs/react-component.html#unsafe_componentwillmountに変更され

ComponentDidMount内のクライアント側ライブラリであるmapbox-glモジュールが必要で、醜いエラーReferenceError: self is not definedを取り除きました。

コンポーネントがサーバー上でまだ処理されようとしているため(正確に何をしようとしているのかわかりません)、 componentDidMountを使用してMapboxを作成できませんでした(@elaichと同じ問題が発生しました) )。

@ aga5tyaによって提案された動的インポートを使用することでうまくいきました🎉。

@vanniewelt :動的にインポートされた非同期コンポーネントがクライアントに読み込まれ、すべてのクライアント側ロジックをそこに配置できます。

また、ComponentWillReceivePropsライフサイクルは、サーバー側にいる間、小道具でリクエストを受け取ります。
この変数が存在するかどうかを確認し、クライアントコードを挿入できます。

libにウィンドウ変数が必要な場合は、動的インポートを使用することをお勧めします。

https://github.com/zeit/next.js/tree/v3-beta/examples/with-dynamic-import

解決策を探してこのスレッドに遭遇した人のために。 上記の[動的インポート]リンクが壊れています。 更新されており、 DynamicImportsにあります。

常にクライアントで実行されることになっているコンポーネントがあり、動的インポートを使用したくない場合(たとえば、ユーザーはそれらを使用する必要があることを忘れることができます)、フック(または同等のcomponentDidMount )を使用できます

import React, { useEffect, useState } from 'react'

function CreateInteraction ({ input }) {
  const [isComponentMounted, setIsComponentMounted] = useState(false)

  useEffect(() => setIsComponentMounted(true), [])

  if(!isComponentMounted) {
    return null
  }

  return <h1>I'm only executed on the client!</h1>
}

すでに良い答えをいくつか追加するだけです。私の現在のプロジェクトは、両方向で多くの条件付きレンダリングを実行するため、このためのコンポーネントを使用します。

import React, { useEffect, useState } from "react";

export interface ConditionallyRenderProps {
    client?: boolean;
    server?: boolean;
}

const ConditionallyRender: React.FC<ConditionallyRenderProps> = (props) => {
    const [isMounted, setIsMounted] = useState(false);

    useEffect(() => setIsMounted(true), []);

    if(!isMounted && props.client) {
        return null;
    }

    if(isMounted && props.server) {
        return null;
    }

    return props.children as React.ReactElement;
};

export default ConditionallyRender;

そして、それを次のように使用します。

<Layout>
    <ConditionallyRender server>
        <p>This is rendered only on server.</p>
    </ConditionallyRender>
    <ConditionallyRender client>
        <p>This is rendered only on client.</p>
    </ConditionallyRender>
</Layout>

ブラウザAPIにアクセスするライブラリを使用している場合は、2番目の引数としてSSR = falseを使用してモジュールを動的にインポートできます。
const DynamicComponentWithNoSSR = dynamic( () => import('../components/hello3'), { ssr: false } )
https://nextjs.org/docs/advanced-features/dynamic-import

ブラウザAPIにアクセスするライブラリを使用している場合は、2番目の引数としてSSR = falseを使用してモジュールを動的にインポートできます。
const DynamicComponentWithNoSSR = dynamic( () => import('../components/hello3'), { ssr: false } )
https://nextjs.org/docs/advanced-features/dynamic-import

@ yashwant-dangiコードを見ている人に「動的」インポートを使用する場合も、このインポートを追加する必要があります。

import dynamic from 'next/dynamic'

@Timerコメントhttps://github.com/vercel/next.js/issues/2473#issuecomment-362119102を編集したことに気付きました。これは、元のコメント編集内容を反映するために再度編集しました。

  1. process.browserだけでなくtypeof window !== 'undefined'を使用する必要があるのはなぜですか? より冗長なので、ここで理由がない場合は、 process.browserを使用することをお勧めします。
  2. 次回投稿者のコメントを編集するときに、コメントで編集内容を明確にできますか? まず、ここで行ったように、スレッドが混乱する可能性があります。次のコメントは、私の特定のコメントへの返信です。 第二に、それは私が書いたものではなく、人々が反応したときに読んだものでもないので、誤解を招く恐れがあります。

process.browserだけでなくtypeofwindow!== 'undefined'を使用する必要があるのはなぜですか? より冗長なので、ここで理由がない場合は、process.browserを使用することをお勧めします。

process.browserは非標準であり、webpack環境でのみ使用できます。つまり、将来的に破損する可能性があります。たとえば、webpack 5はprocessポリフィルしなくなります。

代わるtypeof window !== "undefined"可能性がObject.prototype.isPrototypeOf(window) 。 ただし、この投稿に基づいて、Next.jsによって生成されたバンドルは以前のものを検出し、より軽いバンドルを生成するものはすべて勝利と見なされます。

このページは役に立ちましたか?
0 / 5 - 0 評価