見出し画像

nextjs with typescript:20 SWRの使い方

【1】SWR

SWRはデータフェッチに使うReactHookライブラリの一つ。Next.jsのチームが開発したもの。(名前はHTTP RFC 5861のstale-while-revalidateから由来)

SWRによるデータフェッチは以下のような優先順位になっている。
・まず最初にキャッシュからデータを返す。
・次にフェッチリクエストを送る。
・最後に最新データ取得してくる。

【2】使い方

useSWR()」でデータフェッチをする。この時
・第一引数に「url」を渡す
・第二引数に「axiosの処理」に引き渡す(※axios等のfetcherの処理を書く)

【pages/home.tsx】

import AddVtuber from "../components/AddVtuber"

//import {useEffect, useState} from 'react';
import axios from "axios";
import useSWR from "swr";


const Home = ()=>{

   const {data} = useSWR('http://localhost:4001/persons', (url:string)=> axios(url).then(res => res.data) );
   //変数dataは型定義すべきだが今回は省略
   //dataにundefinedが返ってくる可能性があるが、
   //描画時に「?」をつけて対処data?.map(...)やあるいは(data || []).mapみたいなかき方でも可能

   /*
   const [data, setData] = useState([]);

   useEffect(
       () =>{
           async function loadData(){
               const response = await axios('http://localhost:4001/persons');
               setData(response.data);
           }

           loadData();
       }, []
   );
   */

   return (
       <div>
           <AddVtuber />

       {data?.map(row => (
           <div key={row.id}>
               {row.id}: {row.name}: {row.details}
           </div>
       ))
       
       
       }
       </div>
   );
}

export default Home

これによって、フェッチしたデータを定期的に取得するようになる。(ブラウザにフォーカスがあたったタイミングで動作する)

画像1

■ADDボタンでデータを追加してみる

画像2

今回はaddボタンの処理に「alertをはさんで、ブラウザへのフォーカスを外して、戻すという動きでSWRを起動している」。

 しかし実際のUIでは「alertウィンドウを出さずに、ボタン押下したらすぐ反映(描画される)挙動がよくあるUIの挙動」

 それを実現するのが「トリガー(trigger)」というもの。これは次回説明予定。

【3】<SWRConfig>で第二引数部分を共通化(グローバルコンフィグ)

実際のアプリにおいて、useSWRの中にaxiosの処理をあちこちのコンポーネントに長々と記載するのはなかなか大変。そこでSWRはグローバルコンフィグを用意している

■設定の仕方
<SWRConfig>を「_app.tsx」に記述して<Component {...pageProps} />をラッピングすればよい

【例:pages/_app.tsx】

...(略)...

import { SWRConfig } from 'swr';//SWRのグローバルコンフィグ用
import axios from 'axios';

...(略)...


export default function MyApp(props: AppProps) {

...(略)...

return (
  <React.Fragment>
      ...(略)...

       <SWRConfig 
           value={
               {    
                   fetcher: (url: string) => axios(url).then(r => r.data)
               }
           }
       >

          <Component {...pageProps} />

      </SWRConfig>

      ...(略)...
  </React.Fragment>
);
}

【pages/home.tsx】
SWRのグローバルコンフィグ設定により、useSWRの第二引数を書かなくてよくなる。

const Home = ()=>{

   //const {data} = useSWR('http://localhost:4001/persons', (url:string)=> axios(url).then(res => res.data) )
   const {data} = useSWR('http://localhost:4001/persons');
   
   ...(略)...
 
 

【4】SWRのフェッチタイミングとキャッシュ取得

SWRのフェッチタイミングは「2秒のインターバル」がデフォルト設定。

■準備作業:Material-UIのApp Barをつけたサンプル画面を用意する

説明のため以下のような画面を用意。

画像3

画像4

■App Barの配置
App Barはサンプルをコピペして適当に修正する。

【./components/Nav.tsx】

import { AppBar, Button, makeStyles, Toolbar, Typography } from '@material-ui/core';
import Link from 'next/link';
import React from 'react';

const useStyles = makeStyles(theme => ({
   root: {
     flexGrow: 1
   },
   menuButton: {
     marginRight: theme.spacing(2)
   },
   title: {
     flexGrow: 1
   }
 }));


const Nav=()=>{

   const classes = useStyles();

   return (
   <AppBar position="static">
     <Toolbar variant="dense">
       <Typography variant="h6" className={classes.title}>
         App Bar
       </Typography>

       <Button color="inherit">
         <Link href="/home">
           <a style={{ color: 'white' }}>
             <Typography  color="inherit">
               Home
             </Typography>
           </a>
         </Link>
       </Button>

       <Button color="inherit">
         <Link href="/addvtuber">
           <a style={{ color: 'white' }}>
             <Typography  color="inherit">
               addvtuber
             </Typography>
           </a>
         </Link>
       </Button>
     </Toolbar>
   </AppBar>
   );
}

export default Nav

↓ 
これを各画面に配置するが、各コンポーネントに置くのではなく「_app.tsx」に配置して全体に適用する。

【./pages/_app.tsx】

import React from 'react';
import Head from 'next/head';
import { AppProps } from 'next/app';
import { ThemeProvider } from '@material-ui/core/styles';
import CssBaseline from '@material-ui/core/CssBaseline';
import theme from '../theme'; //ここにグローバルCSSを定義

import { SWRConfig } from 'swr';//SWRのグローバルコンフィグ用
import axios from 'axios';

import Nav from '../components/Nav'; //App Bar用

export default function MyApp(props: AppProps) {
const { Component, pageProps } = props;

React.useEffect(() => {
  // Remove the server-side injected CSS.
  const jssStyles = document.querySelector('#jss-server-side');
  if (jssStyles) {
    jssStyles.parentElement!.removeChild(jssStyles);
  }
}, []);

return (
  <React.Fragment>
    <Head>
      <title>My page</title>
      <meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width" />
    </Head>
    <ThemeProvider theme={theme}>
      {/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
      <CssBaseline />

       {/* App Barを配置 */}
       <Nav/>
       <SWRConfig 
           value={
               {    
                   fetcher: (url: string) => axios(url).then(r => r.data)
               }
           }
       >

          <Component {...pageProps} />

      </SWRConfig>


    </ThemeProvider>
  </React.Fragment>
);
}

■各ページを作成

【./pages/home.tsx】※特に変更なし

import AddVtuber from "../components/AddVtuber"

import useSWR from "swr";


const Home = ()=>{

   const {data} = useSWR('http://localhost:4001/persons');

   return (
       <div>
           <AddVtuber />

       {data?.map(row => (
           <div key={row.id}>
               {row.id}: {row.name}: {row.details}
           </div>
       ))
       
       
       }
       </div>
   );
}

export default Home

【./pages/addvtuber.tsx】
「components/Addvtuber.tsx」をまるごと持ってきて再利用する。

import AddVtuber from "../components/AddVtuber";

export default AddVtuber;

■本題:「2秒のインターバル」がデフォルト設定

画像5

このインターバルを調整するのが「dedupingInterval」プロパティ。
できるだけキャッシュを使わせたいという場合などはこの時間を長くする

【./pages/_app.tsx】:インターバルを5秒(5000ミリ秒)に設定する場合

...(略)...

import { SWRConfig } from 'swr';//SWRのグローバルコンフィグ用
import axios from 'axios';

import Nav from '../components/Nav'; //App Bar用

export default function MyApp(props: AppProps) {

     ...(略)...

return (
  <React.Fragment>
       ...(略)...

       {/* App Barを配置 */}
       <Nav/>
       <SWRConfig 
           value={
               {    
                 dedupingInterval:5000,
                 fetcher: (url: string) => axios(url).then(r => r.data)
               }
           }
       >

          <Component {...pageProps} />

      </SWRConfig>


    ...(略)...
  </React.Fragment>
);
}

もっと応援したいなと思っていただけた場合、よろしければサポートをおねがいします。いただいたサポートは活動費に使わせていただきます。