react-apolloの代わりにapollo-clientを直接使用すると、apolloの統合がはるかに簡単になります。
コードは次のとおりです: https ://github.com/nmaro/apollo-next-example
そして、これが実行中のバージョンです(少なくとも、graphqlサーバーをオンラインに保つ限り): https ://apollo-next-example-oslkzaynhp.now.sh
関連する詳細はここにあります:
apollo.js
import ApolloClient, {createNetworkInterface} from 'apollo-client'
export default new ApolloClient({
networkInterface: createNetworkInterface({
uri: GRAPHQL_URL
})
})
その後、ページで
import React from 'react'
import gql from 'graphql-tag'
import 'isomorphic-fetch'
import apollo from '../apollo'
import Link from 'next/link'
const query = gql`query {
posts {
_id
title
}
}`
export default class extends React.Component {
static async getInitialProps({req}) {
return await apollo.query({
query,
})
}
render() {
...
}
}
いくつかの観察:このアプローチは、コンポーネントに深く入り込んで、遭遇するすべてのgraphqlクエリをロードしません(react-apolloを使用してサーバー側で有効にできるもの)。
これはnext.jsで少し問題があると思います。クライアント側とサーバー側の両方でデータをロードしたい場合は、コンポーネント階層の奥深くにデータをロードすることを意図しているわけではありません。 データをロードするポイントは1つだけです。それは、ルートコンポーネントのgetInitialPropsです。 これが将来変更されるかどうかはわかりませんが、変更されない場合は、変更する必要があります
どちらの場合も、getInitialPropsにロードされるデータには、上記のアプローチで問題ありません。
また、コア開発者がこれを気に入った場合は、例を使用してプルリクエストを作成できます。
ルートでのみ呼び出されるgetInitialPropsについては、 https://github.com/zeit/next.js/issues/192を参照してください。 そこにあなたのアイデアが欲しいです。
@sedubois react-apollo
でどのような問題が発生しましたか?
@nmarohttps ://github.com/nmaro/apollo-next-exampleは空です。
@amccloudはそれについて@nmaroに尋ねたほうがいいです(私はまだコードに戻る必要があります)。
@seduboisに感謝します。これでオンラインになりました(最初push
だけでなく、常にpush origin master
を実行することを忘れてください)。
おっと、私は間違った人に言いました。 @nmaro react-apolloでどのような問題がありましたか?
データはサーバーにロードされ、クライアントがロードを開始するとすぐにページは再び空になりました。 次に、 @ seduboisの実装(https://github.com/RelateMind/relate)を見て、概念実証をすばやく行うにはすでにかなり複雑だと思ったので、最終的に低レベルのAPIを試してみました。
@stubailoは、なぜapolloをnext.jsと統合するのが非常に難しいのか疑問に思っていたので、クライアントとサーバーの両方でデータをフェッチできる唯一の場所は、getInitialPropsと呼ばれる非同期関数内のページのルートコンポーネントにあるようです。 react-apolloを統合する通常の方法は、クライアント側でのみ役立つと思います。
興味深い-Next.jsと他のデータ統合はありますか? 私が見た例に基づくと、Reduxの使用もかなり難しいようです。
最近のほとんどのデータシステムには、ある種のグローバルキャッシュ(Redux、Apollo、Relay)があるため、これを有効にするには、Nextにある種の機能が必要だと思います。
Next.jsを、グローバルキャッシュ(Redux、Apollo、Relay)を備えた最新のデータシステムでより適切に再生するにはどうすればよいですか? これは次のリリースの大きな優先事項になると思います。 @stubailo @rauchg
絶対。 ウィキにReduxの例があります。もっとそのようなものを作成する必要があります:)
ところで、リリースベースでやらなければならないことではありません。 いつでもwikiチュートリアルを書くことができます。
ところで@nmaroその例は本当にきれいに見えます、貢献してくれてありがとう。 それをベースにして拡張することができます
@stubailo最小限の例であなたと一緒に働きたいです。 私はいくつかのプロジェクトでユニバーサルアポロマイクロフレームワークであるSaturnを使用しており、最終的にはそれらをNext.js+Apolloに移植したいと思っています:)
いいですね-ええ、フロントページアプリに最小限の変更を加えて、create-react-appではなくnext.jsで実行できるようにするのが私の好みです。 その後、ホームページにも掲載できます。
@stubailo
小さな問題は、データがサーバーにロードされてレンダリングされ、クライアントにロードするときに何も置き換えられないことでした-私はapolloを知らず、次にそれを正しくするのに十分だと思います。 apollo-clientを直接使用しても、この問題は発生しませんでした。
サーバーレンダリングでさらに注意が必要なのは、階層のより深いところにクエリがある場合です。 Reactには、非同期でレンダリングする方法がありません。つまり、各コンポーネントの準備が整うのを待ってからレンダリングします。 つまり、ssrフレームワークは次のいずれかを行う必要があります
ここで問題となるのは、apolloに、コンポーネントツリーのレンダリングに必要なすべてのデータ呼び出しを検出する方法があるかどうかです。これは、getInitialPropsに提供できる1つの関数呼び出しですべて実行できます。
@stubailoこれに対する解決策はありますか? ^
@nmaro @ ads1018 getDataFromTree
を見たことがありますか? たとえば私の例で使用されているように: https ://github.com/RelateMind/relate/blob/master/hocs/apollo.js
ところで、 https://github.com/zeit/next.js/pull/301がマージされたので、物事を単純化できるのではないかと思います。 まだ調べていません。
@sedubois共有してくれてありがとう! ええ、react-apolloを使用した例は、Masterにマージされたばかりの新しいプログラマティックAPI(#301)で簡略化できるので、すべてのページコンポーネントを独自のHOCでラップする必要はありません。 それで何か進歩があれば、私に知らせてください! apolloホームページでnext.jsの例を入手するのはクールでしょう:)
NB @ ads1018 https://github.com/zeit/next.js/pull/301は、Programmatic APIではなく、CommonsChunkPluginを使用して共通コードを抽出することを目的としています。 しかし、そうです、プログラマティックAPIも間違いなく役立ち、リリースされるのを楽しみにしています。
誰かがreact-apolloを新しい2.0.0-beta.2リリースで動作させる運がありましたか?
@sedubois @stubailoご覧になりたい場合は、next+react-apolloでの試みを押し上げました。 ここで見つけることができます: https ://github.com/ads1018/frontpage-next-app
私が今直面している問題の1つは、コンポーネントがサーバー側ではなくクライアント側でのみレンダリングされることです。 おそらく、server.js内でreact-apolloのgetDataFromTree
メソッドを使用できますか? または多分私たち自身のカスタム<document>
の中に? 任意の提案/プルリクエストは大歓迎です!
最終的に、このhelloworldの例をNextexamplesフォルダーとApolloホームページに含めたいと思います。
データをサーバーでレンダリングするための唯一の前提条件は、データがgetInitialProps
のオブジェクトとして返されることであり、オーバーライドは必要ありません。
ガッチャ。 @nmaroが指摘したように、これはreact-apolloでは少し難しいと思います。
問題は、apolloに、コンポーネントツリーのレンダリングに必要なすべてのデータ呼び出しを検出する方法があるかどうかです。これは、getInitialPropsに提供できる1つの関数呼び出しですべて実行できます。
ガッチャ
@ ads1018少し調べてみると、トップレベルのコンポーネントがgetInitialPropsで公開されていれば、 Apolloヘルパーを使用して文字列にレンダリングできます。
その場合、_documentは次のようになります。
export default class MyDocument extends Document {
static async getInitialProps ({ app }) {
const wrapped = React.createElement(ApolloProvider, { client }, app)
const rendered = await renderToStringWithData(wrapped)
return { html: rendered, initialState: client.store.getState() }
}
render () {
return (
<html>
<Head>
<title>My page</title>
</Head>
<body>
<ApolloProvider client={client}>
<Main />
</ApolloProvider>
<NextScript />
</body>
</html>
)
}
}
@rauchg renderPage
app
を公開するのは簡単な変更のようですが、私が見落としているものはありますか?
@bs1180ああ素晴らしい。 それが私が探していたものです。 うまくいけば、 app
を公開するのは簡単な変更です。 Nextは即座にgraphqlクライアントフレンドリーなフレームワークになります。
@ bs1180 renderPage
returnオブジェクト内のapp
を公開しました。 これはあなたが考えていたものと一致していますか?
@ ads1018完全ではありません-ご使用のバージョンでは、 render
がまだ呼び出されています。これは、 renderToStringWithData
が手動で呼び出される場合は、不要な重複になります。
私はこれについてさらに作業を行いましたが、主にメインアプリが<Main />
コンポーネントの子として(__next divに)レンダリングされているため、最終結果は最初に想像したほどきれいではありません。コンテキストが上からアプリケーションに渡されるのを防ぎます。 したがって、Apolloコンテキストを再度追加するにはHOCが必要です。
@bs1180なるほど。 <Main />
をApolloProvider
の子としてレンダリングして、コンテキストを渡すことはできますか?
どういう意味かわかりませんが、方向が間違っていると思います。 完璧なSSRは、HOCだけで実現できます。これが私の石畳のバージョンを出発点として示しています。
export default (options = {}) => Component => class ApolloHOC extends React.Component {
static async getInitialProps (ctx) {
const user = process.browser ? getUserFromLocalStorage() : getUserFromCookie(ctx.req)
const jwt = process.browser ? null : getJwtFromCookie(ctx.req)
if (options.secure && !user) {
return null // skip graphql queries completely if auth will fail
}
const client = initClient(jwt)
const store = initStore(client)
// This inserts the context so our queries will work properly during the getDataFromTree call,
// as well as ensuring that any components which are expecting the url work properly
const app = React.createElement(ApolloProvider, { client, store },
React.createElement(Component, { url: { query: ctx.query }}))
// this is the most important bit :)
await getDataFromTree(app)
const initialState = {[client.reduxRootKey]: {
data: client.store.getState()[client.reduxRootKey].data
}}
return { initialState, user }
}
constructor (props) {
super(props)
this.client = initClient()
this.store = initStore(this.client, this.props.initialState)
}
render () {
return (
<ApolloProvider client={this.client} store={this.store}>
<Component url={this.props.url} />
</ApolloProvider>
)
}
}
initClient
とinitStore
は、reduxの例をモデルにしています。 すべてのページは次のようになります。
import ApolloHOC from '../hoc'
import { graphql } from 'react-apollo'
export default ApolloHOC({ secure: false })(() => <b>Hello world</b>)
それがお役に立てば幸いです。他に調査する方法があるかどうか、または私が見落としているものがあるかどうかを知りたいです。
@ bs1180かっこいい、これは共有してくれてとても便利です。
_document.js
内にgraphqlデータを含むページをレンダリングできるものは他にありますか? あなたが最初に提案したように、そのHOCをすべて一緒にバイパスできればいいのですが。
私はそうは思いません-私が見ることができることから、クライアント側のレンダリングは、カスタム_document.js
からコンテキスト(Apolloクライアント、標準のReduxストア、テーマなど)で渡されるものをすべて削除します。 一部のApolloSSRロジックはそこに移動できますが、必要なオブジェクトをコンテキストに戻すには、何らかのHOC/ラッパーコンポーネントが必要です。
next.jsの内部についての知識が豊富な人なら、もっと良いアイデアがあるかもしれません。
実例を得ることができたら、ぜひチェックしてみてください。 私はまだこれを機能させるために戦っています。
ReactApolloとNextの実用的な例があります😄🚀多くの人がそれが役立つことを願っています。 https://github.com/ads1018/next-apollo-exampleで確認できます(Nowを使用してデモもデプロイしました)。
ページ内でwithData()
というHOCを使用して、ページをApolloProvider
$でラップすることになりました。 私は当初、単一のファイル内に一度ではなく、ページごとにプロバイダーを使用することでオフになりましたが、読みやすさとスケーラビリティの点で優れていると本当に賢い人々から確信されました。 私は実際、 withData(MyComponent)
は非常に見栄えがよく、特定のページがデータをフェッチするという読者に良いコンテキストを提供すると思います(しゃれは意図されていません)。
私を正しい方向に向けてくれた@bs1180と@rauchgに感謝します。 with-apollo
の例をリポジトリに追加したい場合は、お知らせください。プルリクエストを作成できます。
ありがとう@ads1018😊私の例https://Relate.now.shと比較して、この例は、深くネストされたコンポーネントでApolloを使用する問題を解決しますか(getInitialPropsのカスケードを回避します)? たぶん、この例は、それが主な問題点であることを示しているはずです。 そして、これをexamplesフォルダーに追加していただければ幸いです。
@sedubois #192で参照したエラーを再現できません。 ネストされたコンポーネント内で問題なくApolloを使用しています。 私の例をプルダウンして再現できる場合は、教えていただけますか?
よくできました、 @ ads1018 @sedubois! 私はこれと#192をフォローしてきました。また、ApolloとvanillaReactを使用してプリフェッチ/非同期ビューを調査しています。
各ページが表示される前にgetDataFromTree
を実行すると、パフォーマンスの問題に気づきましたか、または予想しますか? 技術的には、このメソッドはツリー全体を再帰的にレンダリングし、 getInitialProps
が戻ると、Reactはツリーを再度レンダリングします(キャッシュからのデータはありますが)。
本当に素晴らしい解決策👍すべての子データを確実にキャッシュするための唯一のオプションは、2回レンダリングすることだと思います。パフォーマンスについてどう思うか、興味があります。
@ ads1018あなたの例のためのいくつかのアイデア:
typeof window !== 'undefined'
に単純化し、!!ctx.reqを削除します@ ads1018聞いてよかった! ちょっといいデモ。
私が尋ねるつもりだったのは、このスケールはどれくらいうまくいくかということです。 Nextはまだ使用していませんが、理解しているように、Nextは、ページコンポーネント、つまりpages/page.js
で利用可能な場合、各ルート遷移でgetInitialProps
を呼び出します。 数百のノードと大量のデータが入ってくるフルスケールのアプリ/ウェブサイトでは、各ルートで2回レンダリングすると、ある程度の遅延が発生する可能性があると思います。
私が取り組んでいるプロジェクトは大規模な編集サイトなので、あなたのアプローチも含めて、さまざまなアプローチのベンチマークを行いたいと思っています。 よろしければ、ツイッターでもっと話し合いたいです。 あなたの仕事をありがとう!
@estrattonbaileyGotcha 。 私はそれが非常にうまくスケーリングすると思います。 最初のページの読み込みでは、 getInitialProps
はサーバーでのみ実行されます。 getInitialProps
がクライアントで再度実行されるのは正しいですが、 getDataFromTree
はサーバー上にあるかどうかをチェックする条件内にラップされているため、データが2回要求されることはありません。
補足-ページで多くのコンポーネントとデータが要求されているために最初のページの読み込み時間が心配な場合は、SSR中に特定のクエリを意図的にスキップし、 ssr: false
を渡すことで、それらをクライアントにオフロードするようにapolloにいつでも指示できます。 apolloクエリオプションの
さらに話し合いたい場合は、ツイッターで連絡します:)
getInitialPropsがクライアントで再度実行されるのは正しいですが、getDataFromTreeはサーバー上にあるかどうかをチェックする条件内にラップされているため、データが2回要求されることはありません。
getInitialProps
は、初期ロード後ではなく、 <Link>
で移行する場合にのみ、クライアント側で実行されることに注意してください。
@ ads1018 @estrattonbailey AFAIK確かに、最初のページの読み込み時にサーバー側で2つのレンダリングがあります。getDataFromTreeが実行され、ツリー全体が内部でレンダリングされた後、レンダリングが再度呼び出されてHTML応答が作成されます。 それを回避する方法があるかどうかは考えないでください。ただし、SSRによって回避されたネットワークのラウンドトリップのおかげで、それでもかなりのパフォーマンスが得られると思います。
GraphQLサーバーがNext.jsサーバーと同じマシンでホストされている場合にパフォーマンスが最大になると思います。したがって、パフォーマンスに関心がある場合は、いつでも試してみることができます(この時点で、Graphcoolを使用してアプリのプロトタイプを作成します。 Next.jsがNow/Zeit Worldでデプロイされている間、バックエンド)。
@sedubois @estrattonbailey間違っている場合は訂正してください。ただし、レンダリングは1回だけです。 getDataFromTree
はツリーをレンダリングせず、Apolloクライアントストアでデータの準備ができたときに解決されるpromiseを返すだけです。 promiseが解決した時点で、Apolloクライアントストアは完全に初期化され、HTTPリクエストの応答で文字列化された結果を渡したい場合は、オプションでツリーをレンダリングできますが、私の例ではそれを行っていません。
getDataFromTreeはツリーをレンダリングしません
@ ads1018 AFAIKとApolloコードを見ると、ツリーを再帰的にレンダリングします(Apolloデータフェッチをトリガーするためだけです)。 つまり、最初のページの読み込み時にサーバー側で2回レンダリングされます。
しかし、とにかく、デモのおかげで、ApolloとNextの間に使用可能な統合ができました。また、Apollo SSRのパフォーマンスに関する残りの質問には、Next.jsに固有の質問はもうありません。 あちらでそれについて質問することをお勧めします。
@seduboisとにかくレンダリングとは何ですか? 私はそれを歩いて木を振ると呼ぶでしょう。 かなりうまく最適化されているようです– setState
が抑制され、DOMへの調整はありません。
@ ads1018良い例です! ここでもWikiに追加されているようですので、この問題はおそらく解決される可能性がありますか?
cc @rauchg
ApolloブログでApollo+Next.jsに関するブログ投稿を取得する必要があります!
@stubailo @ ads1018の例は素晴らしいです👏同じApolloの原則を使用してより大きなものについては、私のアプリをチェックできます: https ://github.com/relatenow/relate
@helferに感謝します。 私はそれがどのように出てきたかにわくわくしています。 Next.js+Apolloでアプリ開発の聖杯を発見したような気がします。 私は福音を広めるためにブログ投稿をフォローアップするつもりでしたが、それを理解できていません。 @stubailoApolloミディアムパブリケーションの作品でコラボレーションできれば幸いです:)
例と彼の甘いアプリを手伝ってくれた@seduboisに大声で叫びます。 😄
@ads1018はあなたをブログに載せたいと思っています。 それについてチャットする準備ができたら、Apollo Slackで私(thea)にpingを送信します。 :)
@helferあなたは完全に正しいです。 問題を解決できるかどうかを確認するために、新しい問題パスを実行する必要があります😄
@stubailo@ theadactyl素晴らしいアイデア❤️
サーバー側でデータを2回要求することに関して、注目すべき問題/ PRを知っている人はいますか? ちょっと興味があるんだけど
最も参考になるコメント
ApolloブログでApollo+Next.jsに関するブログ投稿を取得する必要があります!