質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.47%
Next.js

Next.jsは、Reactを用いたサーバサイドレンダリングなどを行う軽量なフレームワークです。Zeit社が開発しており、nextコマンドでプロジェクトを作成することにより、開発環境整備が整った環境が即時に作成できます。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

Q&A

1回答

250閲覧

next.js データを取得する関数が使えない

mksmkss

総合スコア4

Next.js

Next.jsは、Reactを用いたサーバサイドレンダリングなどを行う軽量なフレームワークです。Zeit社が開発しており、nextコマンドでプロジェクトを作成することにより、開発環境整備が整った環境が即時に作成できます。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

0グッド

0クリップ

投稿2024/02/19 07:02

実現したいこと

getEquioment()で取得したitemsDataをitemsDataProviderに渡したいです

発生している問題・分からないこと

いろいろなことを試したもののうまくitemsDataを取得する方法がわかりません

エラーメッセージ

error

1Error: async/await is not yet supported in Client Components, only Server Components. This error is often caused by accidentally adding `'use client'` to a module that was originally written for the server.

該当のソースコード

getEquipmentinfo.ts

1'use server'; 2import { JWT } from 'google-auth-library'; // 認証用。 3import { GoogleSpreadsheet } from 'google-spreadsheet'; 4import { itemProps } from 'const/equipmentType'; 5 6// sheetのインスタンスを取得 7const getSheets = () => { 8 // まず認証情報を作る 9 10 // 見られるスコープはどこまでか 11 const scopes = [ 12 'https://www.googleapis.com/auth/spreadsheets', 13 'https://www.googleapis.com/auth/drive.file', 14 ]; 15 // スプシのシートID process.envうんたらは、.env.localに書いてある 16 const sheetId = process.env.SHEET_ID; 17 // サービスアカウントのメアドとパス 18 const clientEmail = 19 process.env.GOOGLE_SERVICE_ACCOUNT_EMAIL; 20 const privateKey = ( 21 process.env.GOOGLE_PRIVATE_KEY as string 22 ).replace(/\\n/g, '\n'); 23 24 // 認証インスタンス作る 25 const jwt = new JWT({ 26 email: clientEmail, 27 key: privateKey, 28 scopes: scopes, 29 }); 30 31 // シートIDと認証情報をセットにして、spreadsheetのインスタンス生成 32 const doc = new GoogleSpreadsheet(sheetId as string, jwt); 33 return doc; 34}; 35 36// itemProps[]型のデータを取得する関数 37export const getEquipmentInfo = async () : Promise<itemProps[]> => { 38 // シートインスタンスつくって 39 const sheet = getSheets(); 40 // GCPからデータ取得 41 await sheet.loadInfo(); 42 // "equipments"というシートを取得 43 const sheetInfo = sheet.sheetsByTitle['equipments']; 44 45 // シートから列データ取得 46 const rows = await sheetInfo.getRows<itemProps>(); 47 console.log(rows); 48 if (rows) { 49 // 辞書の配列の形式にして、各データを返す 50 return rows.map((row) => { 51 return { 52 name: row.get('name'), 53 count: row.get('count'), 54 tag: row.get('tag'), 55 location: row.get('location'), 56 imagePath: row.get('imagePath'), 57 latestUpdate: row.get('latestUpdate'), 58 purchasePlace: row.get('purchasePlace'), 59 threshold: row.get('threshold'), 60 }; 61 }); 62 } else { 63 // ここでエラーを返すようにすることでitemsがundefinedになるのを防ぐ. 64 // 参考: https://zenn.dev/estra/articles/ts-with-promise-type-annotation#promise-%E3%81%AE%E5%9E%8B%E6%B3%A8%E9%87%88 65 return []; 66 } 67}

itemsContext.tsx

1'use client'; 2import { ReactNode, createContext, use, useEffect, useState } from 'react'; 3import { itemProps } from '@/lib/const/equipmentType'; 4import { getEquipmentInfo } from '@/lib/apis/getEquipmentInfo'; 5 6export const ItemsDataContext = createContext({ 7 itemsData: [] as itemProps[], 8}); 9 10export const ItemsDataContextProvider = ({ 11 children, 12}: { 13 children: ReactNode; 14}) => { 15 const [itemsData, setItemsData] = useState<itemProps[]>([]); 16 useEffect(() => { 17 const fetchData = async () => { 18 const data = await getEquipmentInfo(); 19 setItemsData(data); 20 }; 21 fetchData(); 22 }, []); 23 return ( 24 <ItemsDataContext.Provider value={{ itemsData }}> 25 {children} 26 </ItemsDataContext.Provider> 27 ); 28};

試したこと・調べたこと

  • teratailやGoogle等で検索した
  • ソースコードを自分なりに変更した
  • 知人に聞いた
  • その他
上記の詳細・結果
  • リストPage Routerではなく,App Routerであるため,getStaticPropsなどが使えないこと
  • リストServer Actionで行けるかと思い,試したものの,

It is not allowed to define inline "use server" annotated Server Actions in Client Components. To use Server Actions in a Client Component, you can either export them from a separate file with "use server" at the top, or pass them down through props from a Server Component.

と言われてしまう

補足

参考記事
getEquipment内のconsole.logからデータが取得されていることはわかっています
どうぞよろしくお願いいします

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

2024/02/20 追記: コードに誤りがあったので、修正しました。

Pages Router の getStaticProps / getServerSideProps に相当するのは、Server Component です。

  • fetch() を呼び出すときにオプションを指定
  • unstable_noStore() を呼び出す
  • cookies() を呼び出す

などの「動的な機能」を使ったときのみ「動的レンダリング」となり、getServerSideProps に相当、使っていないときは「静的レンダリング」となって、getStaticProps に相当します。

https://nextjs.org/docs/app/building-your-application/rendering/server-components#switching-to-dynamic-rendering


とりあえず、page.tsx を Server Component にしてそこで使うなら、こんな感じになります。

tsx

1// page.tsx か layout.tsx 2import { ReactNode, use, useEffect, useState } from 'react'; 3import { getEquipmentInfo } from '@/lib/apis/getEquipmentInfo'; 4 5const Page = async () => { 6 const data = await getEquipmentInfo(); 7 8 return ( 9 <ItemsDataProvider value={{ itemsData }}> 10 {children} 11 </ItemsDataProvider> 12 ); 13}; 14 15export default Page;

tsx

1"use client"; 2 3import { FC, ReactNode, createContext, useContext } from "react"; 4import { itemProps } from '@/lib/const/equipmentType'; 5 6type ItemDataContextValue = { 7 items: ItemProps[]; 8}; 9 10const ItemsDataContext = createContext<ItemDataContextValue>({ items: [] }); 11 12export const ItemsDataProvider: FC<{ 13 value: ItemDataContextValue; 14 children: ReactNode; 15}> = ({ value, children }) => { 16 return ( 17 <ItemsDataContext.Provider value={value}> 18 {children} 19 </ItemsDataContext.Provider> 20 ); 21}; 22 23export const useItemsData = () => { 24 return useContext(ItemsDataContext); 25}; 26 27

投稿2024/02/19 12:01

編集2024/02/20 13:11
honey32

総合スコア169

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mksmkss

2024/02/20 00:20

回答ありがとうございます。試してみましたが解決できませんでした。 getStaticPropsなどがServer Componetnに相当することは理解することができました. しかし,教えていただいた通りapp/equipments/layout.tsxを追加,変更してみたのですが Error: Could not find the module "/Users/****/Coding/Next/equipment-manager/src/context/itemsContext.tsx#ItemsDataContext#Provider" in the React Client Manifest. This is probably a bug in the React Server Components bundler. とエラーが出るようになりました.https://github.com/vercel/next.js/issues/50243 によると,親コンポーネントに'use client'と書けば良いとあります.しかし,そのようにした場合,当該ファイルは子ファイルに当たるためserver componetとして利用できないように感じます.どうすれば良いのでしょうか 主なファイル構成は以下のようになります src/app/equipments/layout.tsx : Serve Component化(?),Providerをここに src/app/equipments/page.tsx : Client Componet フロント src/context/itemsContext.tsx : createContextだけに変更 また,awaitはasyncがないと使えないという認識なのですが,page.tsx/layout.tsxで書く場合, const Page = async () => { と書くのが正しいのでしょうか. 拙い文章で申し訳ありません,よろしくお願いいたします.
honey32

2024/02/20 12:56 編集

すみません、僕の回答で2点不備があったので、訂正します。 1. async のつけ忘れ。 ご指摘の通り、Component の中で await するためには async が必要です。 2. Provider の export のしかたについて ItemsDataContext.Provider は、たとえ "use client" をファイル先頭につけていても Server Component からは使用不可能なようです。(関数コンポーネントじゃなくて内部ではクラスコンポーネントになっているのかも) 自力で関数型コンポーネントを作るのが必須だったようです。以下のように書けば、 value を渡せるような ItemsDataProvider になり、これであれば Server Component 側から利用できるようになります。 "use client"; import { ReactNode, createContext, use, useEffect, useState } from 'react'; export const ItemsDataContext = createContext({ itemsData: [] as itemProps[], }); export const ItemsDataProvider: FC<{ value:itemProps[]; children: ReactNode; }> = ({ value, children }) => { return ( <ItemsDataContext.Provider value={value}> {children} </ItemsDataContext.Provider> ); };
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問