LoginSignup
2
1

More than 3 years have passed since last update.

Reactでページ内遷移のある横スクロールスライドメニューを実装する

Posted at

はじめに

スマホ向けのWebアプリケーションをReactで開発していて、横スクロール型のスライドメニュー(ナビゲーション)を作りたい!という時にご参考ください。 React×Railsで開発しています。

HTMLのアンカータグで#を使って特定のid属性に遷移させたい!それをReactでやりたい!みたいな時を想定。

やりたいこと

  1. アプリライクな横スクロール型のメニューを作りたい(スクロールバー非表示にして)
  2. メニューの各要素をクリックしたらそれぞれの要素までぬるっとアニメーション付きでページ内遷移させたい
  3. ヘッダーの高さを考慮してページ内遷移させたい
  4. mapではき出した動的な要素にid属性を振って遷移先にしたい

方針

Reactでページ内遷移するいい方法ないかなーと探していたところ、こちらの記事(Reactでページ内リンクを実装する)を発見。今回はこちらを参考にreact-router-hash-linkを使うことに。

Githubはこちら:React Router Hash Link

前提として本実装では、itemCategorycategory_nameがカテゴリ名で、それをメニューバーにし、同一ページ内のそれぞれのcategory_nameの部分に遷移させています。

実装

1. yarn add かnpmでインストール

Terminal
npm install --save react-router-hash-link

2.react-router-hash-linkMemoryRouter(後述するエラーが出たため)をimport

hoge.tsx
import {MemoryRouter} from 'react-router-dom';
import {HashLink} from 'react-router-hash-link';

3.遷移元となる横スクロールのメニューを作成

smoothをつけるだけでぬるっといい感じの遷移に

HashLinkの機能。とても便利。

href=#hogeになるようにid設定

JSX記法でto={'#' + itemCategory.category_name}とした。これで#hogeとなり、idに遷移できる。

謎のエラーをMemoryRouterで解決

HashLinkを使ったところ、Uncaught Error: Invariant failed: You should not use <Link> outside a <Router>のエラーが表示されたため、こちらの記事(解決方法: "Error: Invariant failed: You should not use outside a ")を参考に、MemoryRouterHashLinkを囲う。

ヘッダーの高さ調整

scroll={el => { el.scrollIntoView(true); window.scrollBy(0, -160) }}でヘッダー部分の高さを考慮。この場合は160px分下げている。

hoge.tsx
<div className={classes.scrollMenuList}>
  <div className={classes.scrollMenuListMask}>
    {props.shopItemCategories.map((itemCategory) => (
      <Typography component="h2" variant="h3" >
    <MemoryRouter>
      <HashLink 
        smooth to={'#' + itemCategory.category_name} 
        scroll={el => { el.scrollIntoView(true); window.scrollBy(0, -160) }}
        className={classes.scrollMenu}>{itemCategory.category_name}
      </HashLink>
    </MemoryRouter>
  </Typography>
  ))}
 </div>
</div>

4.スクロールバーを非表示に

こちらの記事(Appleに学ぶ、横スクロールナビを組む時のCSSメモ)を参考に、MaskとなるscrollMenuListMaskを用意し、親要素のscrollMenuListoverflow:"hidden"としラッパーする。各要素はアンダーバーがダサいので、textDecoration:"none"に。

hoge.tsx
   scrollMenuList: {
      width: '900px',
      overflow: "hidden",
      height: 60,
    },
    scrollMenuListMask: {
      overflowX: "auto",
      whiteSpace: "nowrap",
      display: 'flex',
      height: 80, //Maskのheightを親要素より高くすることがミソ
    },
    scrollMenu: {
      margin: theme.spacing(1, 2, 1),
      fontSize: 36,
      color: "#8f8f8f",
      textDecoration: "none"
    },

5.遷移先にid属性を振る

id属性を振るだけなので簡単。ここで振ったidを遷移元となるHashLinkで指定することでページ内遷移できる。

hoge.tsx
{props.shopItemCategories.map((itemCategory) => (
 <div className={classes.root}>
   <Typography component="h2" variant="h3" id={itemCategory.category_name}>
    {itemCategory.category_name}
   </Typography>
 </div>
...省略
))}

完成

完成したものが以下の通り。

hoge.tsx
//Import
import {MemoryRouter} from 'react-router-dom';
import {HashLink} from 'react-router-hash-link';

//該当部分のみのCSS
const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    scrollMenuList: {
      width: '900px',
      overflow: "hidden",
      height: 60,
    },
    scrollMenuListMask: {
      overflowX: "auto",
      whiteSpace: "nowrap",
      display: 'flex',
      height: 80,
    },
    scrollMenu: {
      margin: theme.spacing(1, 2, 1),
      fontSize: 36,
      color: "#8f8f8f",
      textDecoration: "none"
    },
  })
)

//スクロールスライドメニューとなる部分
<div className={classes.scrollMenuList}>
  <div className={classes.scrollMenuListMask}>
    {props.shopItemCategories.map((itemCategory) => (
      <Typography component="h2" variant="h3" >
    <MemoryRouter>
      <HashLink 
        smooth to={'#' + itemCategory.category_name} 
        scroll={el => { el.scrollIntoView(true); window.scrollBy(0, -160) }}
        className={classes.scrollMenu}>{itemCategory.category_name}
      </HashLink>
    </MemoryRouter>
  </Typography>
  ))}
 </div>
</div>

//メニューからの遷移先になる要素たち
{props.shopItemCategories.map((itemCategory) => (
<>
 <Box>
   <div className={classes.root}>
     <Typography component="h2" variant="h3" id={itemCategory.category_name}>
     {itemCategory.category_name}
   </Typography>
  </div>
 </Box>

...省略
</>
))}

今回も色々な記事を参考にさせていただきました。横スクロールのスライダーみたいなのは結構使いたい場面多いと思うので、少しでも参考になれば嬉しいです。

参考

横スクロールナビゲーションを実装する3つの方法
Reactでページ内リンクを実装する
【React Router】画面遷移時に#を用いて特定の要素に移動させる方法
Reactで動的に属性の値を生成する方法

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1