Reactでスライドを作った時に地味に詰まったので
当方、Reactは初心者。
勉強がてらスライダーをslickプラグインを使ってレイアウト整えていたのですが
矢印の調整で地味に躓きました。
Slick自体はvanillaやjqueryで触ったことがあったのですが、
やっぱり構造違うので書き方変わりますよね。
一部妥協した部分はあります。
導入についてはいろんな記事サイト様で落ちているので割愛します。
やりたかったこと
スライダーの下にドットインジケーターと矢印を並べておきたかった。
[スライダー]
<・・・>
↑みたいな感じで。
横並びのカスタムは本当はこんな感じで
<div className="slide-arrow__box">
<PrevArrow />
<NextArrow />
</div>
jQueryみたくカスタムクラスの中に入れたかったけど、矢印の機能が動かなくなってしまったので挫折。
今はcssでゴリ押しで並べました。
そして、スライドの枚数を取得して、最初と最後の時だけ矢印を非活性化したい。
ここはできたのでその時のお話。
カスタマイズ
改めて見るとやってることは単純なカスタマイズなんだよなぁ...なんて思ったり...
とりあえずjsと違うなと思ったのはこの辺り。
useStateやonClickでの管理...だと思っています。
import React, { useState } from 'react';
import Slider from 'react-slick';
import "slick-carousel/slick/slick.css";
import "slick-carousel/slick/slick-theme.css";
const PrevArrow = (props: any) => {
const { onClick, currentSlide } = props;
return (
<div
className={`slide-arrow prev-arrow ${currentSlide === 0 ? 'disabled' : ''}`}
//最初のスライドだったら矢印押せない
onClick={currentSlide === 0 ? undefined : onClick}
>
</div>
);
};
const NextArrow = (props: any) => {
const { onClick, currentSlide, slideCount } = props;
return (
<div
className={`slide-arrow next-arrow ${currentSlide === slideCount - 1 ? 'disabled' : ''}`}
//最後のスライドだったら矢印押せない
onClick={currentSlide === slideCount - 1 ? undefined : onClick}
>
</div>
);
};
矢印を画像で入れたかったのでカスタムクラスで使えるようにしています。
さらに、スライド枚数を取得し、最初と最後のスライドの時はそれぞれ矢印を押せないようにするための設定も入れています。
作成コンポーネントがNews記事だったため、その名残でnewsDataが残っているのですが...
スライダー説明に不要だったのでその部分は省略しています
ご了承いただきたい...
スライダー部分の流れははこんな感じ
const NewsSlider:React.FC = () => {
const [currentSlide, setCurrentSlide] = useState(0);
const settings = {
dots: true,
infinite: false,
speed: 900,
slidesToShow: 1,
slidesToScroll: 1,
arrows: true,
prevArrow: <PrevArrow currentSlide={currentSlide} />,
nextArrow: <NextArrow currentSlide={currentSlide} slideCount={newsData.length} />,
beforeChange: (oldIndex: number, newIndex: number) => {
setCurrentSlide(newIndex);
},
fade: true
};
return(
<div className="news">
<Slider {...settings}>
{newsData.map((news,index) => (
<div key={index}>
{/*ここにスライダーの中身*/}
</div>
))}
</Slider>
</div>
)
}
カスタム矢印のクラス設定などは以下
arrows: true,
prevArrow: <PrevArrow currentSlide={currentSlide} />,
nextArrow: <NextArrow currentSlide={currentSlide} slideCount={newsData.length} />,
currentSlide={currentSlide} slideCount={newsData.length}
でスライドの枚数を取得しています。
特にスライド枚数を取得しなくて良いのであれば、
<PrevArrow/>
・<NextArrow/>
などの書き方で良いと思います。
スライドの取得について
やってる手順はこんな感じで
スライドの追跡はreact-slickのbeforeChange
イベントを使うとスライド移動前の情報を取得できるとのこと
- 表示されているスライドのindexを
useState
で管理(監視) -
beforeChange
イベントを使い、スライドの変更を検知・状態を更新 - 最初のスライドだった場合、最後のスライドだった場合それぞれ矢印を押せなくする
イベントの取得と更新についてはこんな感じで
古いスライド(表示されている)と新しいスライドのインデックスを設定しているsetCurrentSlideに渡しているという感じ
beforeChange: (oldIndex: number, newIndex: number) => {
setCurrentSlide(newIndex); // ここで次のスライドを更新している
},
あとはcssで非表示の状態を追加すればクリックできなくなります。
.slide-arrow.disabled {
pointer-events: none;
}
一通り作って思ったこと
書き方変わるだけで超苦戦。
一番わかりやすいのはやっぱり公式だった。
https://react-slick.neostack.com/docs/get-started
矢印の位置はサンプルのこの方法でもしかしたらいけるかなーと思っているので再挑戦します。
https://react-slick.neostack.com/docs/example/previous-next-methods
再挑戦しました(改訂版へ↓)
改訂版
当日に書き直します。
改訂版です。
公式のサンプルソースを見ながらあれやこれやしました。
import React, { useRef, useState, useEffect } from "react";
import Slider from "react-slick";
import "slick-carousel/slick/slick.css";
import "slick-carousel/slick/slick-theme.css";
function NewsSlider() {
const [currentSlide, setCurrentSlide] = useState(0);
const [slideCount, setSlideCount] = useState(0);
const sliderRef = useRef<Slider | null>(null);
const NextArrow = () => {
sliderRef.current.slickNext();
};
const PrevArrow = () => {
sliderRef.current.slickPrev();
};
const settings = {
dots: true,
infinite: false,
speed: 900,
slidesToShow: 1,
slidesToScroll: 1,
fade: true,
beforeChange: (oldIndex: number, newIndex: number) => {
setCurrentSlide(newIndex);
},
afterChange: () => {
if (sliderRef.current) {
setSlideCount(sliderRef.current.props.children.length);
}
}
};
const slideData = [
//ここにスライドリスト表示させる情報
];
useEffect(() => {
if (sliderRef.current) {
setSlideCount(sliderRef.current.props.children.length);
}
}, []);
return (
<div className="news slider-container">
<Slider
ref={sliderRef}
{...settings}
>
{slideData.map((news, index) => (
<div key={index}>
//ここにスライドリストの一覧が
</div>
))}
</Slider>
<div className="slide-arrow__box">
<button
className={`slide-arrow prev-arrow ${currentSlide === 0 ? "disabled" : ""}`}
onClick={previous}
disabled={currentSlide === 0}
>
</button>
<button
className={`slide-arrow next-arrow ${currentSlide === slideCount - 1 ? "disabled" : ""}`}
onClick={next}
disabled={currentSlide === slideCount - 1}
>
</button>
</div>
</div>
);
}
export default NewsSlider;
スライダー周りのソースベースは公式です。
公式サイト見たら矢印の部分はuseRefを使っていたので
一回作ったのと全く違う使い方...むしろこっちの方がシンプルなのでは??となりました。
最初にやりたかったコンテナの中に矢印格納する方法もできたので。
いやぁ、無知って罪ね。
単純に矢印おくだけだったら公式の方法を見て、画像なりをcssとかで設置で完了できる。
ただ問題は、矢印の非活性化の部分。
ここ、矢印をカスタムクラスにしていないので取得の方法がちょっと変わってしまった。
むしろこっちの方がややこしくなった。
ややこしくなったところ
- 最初は
beforeChange
だけで済んでいたがafterChange
も追加した - onClickに設定してたものをクラスのところに突っ込んだ
- useEffectで初回ロード時にスライドの総数を見繕うまでの手順を探すググる力のなさ
- 凄絶なAny・null祭りに遭遇する
afterChangeは別に問題はない....beforeChangeと大した変わらないので...
onClickも最初設定が効かなくて、「あれ、この状態じゃさっきのソースそのまま活かせないしクラスで非活性化の状態付与したいし...」となってクラスに突っ込んだ...
何より、凄絶なany・null祭り
最初にconst sliderRef = useRef<any>(null);
で書いていけるのかと思ったけどそんなことはなく。
ググり力も乏しくなんでやねん状態になりまくってた
そんな時に見つけたのがこちらの記事だった...
useRefって... 私にはまだハードルたか... そんなに書き方あったんだと...
ユニオン型でなんとかいけました。
最後に
ユニオン型ってなんだよぉぉ