クモのようにコツコツと

フロントエンドエンジニア イイダリョウの技術ブログ。略称「クモコツ」

【React】styled-componentsにフックの値を設定し、動的にスタイルを変更(ジャンプ率ジェネレーター完成!)

Reactジャンプ率ジェネレーターの続きです。前回は独自フックで関数を抽出し、全てのツマミが動いくようになりました。今回はこのツマミの値をstyled-componentsに埋め込んで、動的にCSSスタイルを変更させたく。それではいきましょう!

【目次】

※参考:前回記事
【React】独自フックで関数を抽出、全てのツマミが動いた!(ジャンプ率ジェネレーター) - クモのようにコツコツと

※参考:ReactでWebアプリを作るシリーズまとめ
qiita.com

前回のおさらい

独自フックで関数を抽出し、全てのツマミが動いくようになった!

https://cdn-ak.f.st-hatena.com/images/fotolife/i/idr_zz/20210330/20210330061009.jpg

※参考:【React】独自フックで関数を抽出、全てのツマミが動いた!(ジャンプ率ジェネレーター) - クモのようにコツコツと

今回はこのツマミの値を元にCSSを動的に変更させたい。

styled-componentsを動的に変える事例(propsを使う方法)

styled-componentsを動的に変えている事例を調べると下記のような内容があった。

※参考:[React & TS] styled-componentsで動的にスタイルを変更する - Qiita

※参考:styled-components: Basics

Adapting based on props
You can pass a function ("interpolations") to a styled component's template literal to adapt it based on its props.

小道具に基づいて適応  
関数(「補間」)をスタイル付きコンポーネントのテンプレートリテラルに渡して、その小道具に基づいて適合させることができます。

小道具ってpropsのことね。propsからinputの値を渡して、その値を条件に三項演算子で分岐処理する。

propsでの動的な変更は以前やっている。

※参考:【React】コンポーネントの属性値を読み込む(style、テキスト、四則演算) - クモのようにコツコツと

※参考:コンポーネントと props – React

しかし、propsを使った事例はinputタグへの入力値によって、あらかじめ設定したスタイルを切り替える内容のようだ(例えば値が1000以上だったら文字を黒色から赤色にする、など)


今回、自分がやりたいのはinputタグの値そのものをCSSの値に入れること。前回、フックを使ってpタグに値を入れたように。

<section>
          <h2>行長</h2>
          <p>値:{lineLength}</p>
          <InputRange type="range" name="range" min="10" max="50" defaultValue={lineLength} onChange={ChangeLineLength}></InputRange>
        </section>

※参考:【React】独自フックで関数を抽出、全てのツマミが動いた!(ジャンプ率ジェネレーター) - クモのようにコツコツと

そのためpropsは使わなかった。

これまで、styled-componentsは関数コンポーネントの外に設定しておりフックは関数コンポーネントの中に設定していた。

styled-componentsを関数コンポーネントの中に設定してもいいんだ、という意味では上記の事例は参考になった!

Settingコンポーネントのタグを変更

Settingコンポーネント

      <Setting>
        <dl>
          <dt>
            行長:{lineLength}文字
            <span className="css">( max-width: {lineLength}em; )</span>
          </dt>
          <dd>
            <input type="range" name="range" min="10" max="50" defaultValue={lineLength} onChange={ChangeLineLength} />
          </dd>
          <dt>
            行間:{lineHeight}倍
            <span className="css">( line-height: {lineHeight}em; )</span>
          </dt>
          <dd>
            <input type="range" name="range" min="1" max="3" step="0.05" defaultValue={lineHeight} onChange={ChangeLineHeight} />
          </dd>
          <dt>
            ジャンプ率:{Math.round(jumpRate * 100)}%
            <span className="css">( font-size: {jumpRate}em; )</span>
          </dt>
          <dd>
            <input type="range" name="range" min="1" max="4" step="0.05" defaultValue={jumpRate} onChange={ChangeJumpRate} />
          </dd>
        </dl>
      </Setting>
  • 前回section、h2、pタグの構成だったが、dl、dt、ddタグの構成に変更(コンパクトな見た目にするため)
  • 前回は「行長:n」など数値のみ表示していたが「行長:n文字」など単位も追加(dtタグ)
  • さらにdtタグの中のspanタグ.css部分にcssの設定値も追加
  • 行間は最大値maxを2.5から3に変更、しきい値stepを0.01から0.05に変更
  • ジャンプ率の最大値、最小値を3桁から1桁に変更、しきい値stepを0.01から0.05に変更

ジャンプ率は「%」表示は3桁だがCSSはemで1桁。そのためinputの設定値は1桁にして* 100で3桁にした。

たまに割り切れない時があったため、Math.round()で小数点を切り上げをした。

※参考:JavaScriptで四捨五入、切り捨て、切り上げする方法

フックの初期値を変更

ジャンプ率のフックの初期値を変更

  const [lineLength, setLineLength] = useState(35);
  const [lineHeight, setLineHeight] = useState(1.75);
  const [jumpRate, setJumpRate] = useState(2); // 200から2に変更
  • 変数jumpRatesetJumpRateuseState()の引数を200から2に変更

先ほどのinputタグの設定値を1桁に変更したため。

Settingコンポーネントのスタイルを設定

スタイル設定もdldtddタグの形で設定する

const Setting = styled.div`
   margin: 30px 0;
   dt {
     font-weight: bold;
     .css {
       font-weight: normal;
     }
   }
   dd {
     margin: 0 0 1em;
   }
   input {
     width: 100%;
     max-width: 600px;
   }
 `;
  • Settingコンポーネントはdivタグにする
  • dtタグは太字だが中の.cssはノーマルな太さ
  • inputタグの幅はSPに対応するため基本100%、最大600pxにする

こんな感じのコンパクトな見た目になった! f:id:idr_zz:20210406063950j:plain

Exampleコンポーネントを作成

inputタグのツマミを変えた時に動的に見た目を返るサンプル部分のコンポーネントを作る。

      <Example>
         <Section>
           <H2>タイトルです、ああタイトルです、タイトルです</H2>
           <P>本文です。本文です。本文ですったら、本文です。本文です。本文です。本文ですったら、本文です。本文です。本文です。本文ですったら、本文です。本文です。本文です。本文ですったら、本文です。本文です。本文です。本文ですったら、本文です。本文です。本文です。本文ですったら、本文です。本文です。本文です。本文ですったら、本文です。本文です。本文です。本文ですったら、本文です。本文です。本文です。本文ですったら、本文です。</P>
         </Section>
       </Example>
  • Exampleコンポーネントの中にSectionH2Pコンポーネントが入っている

試行錯誤の結果こうなった。一番外側のExampleコンポーネントに静的なスタイルを設定し、内側のSectionH2Pコンポーネントには動的なスタイルを設定する。動的なスタイルは関数コンポーネントの中に書く。

Exampleコンポーネントの静的なスタイルを設定

Exampleコンポーネントに静的なスタイルを設定する

const Example = styled.div`
   background: #eee;
   display: inline-block;
   border-radius: 10px;
   font-size: 14px;
   section {
     margin: 30px;
     line-height: 1.75em;
     h2 {
       color: #000;
       line-height: 1.25em;
       margin: 0 0 1em;
     }
     p {
       margin: 0;
     }
   }
 `;

これらの設定はいつものように関数コンポーネントの外に書いている。

P、Section、H2コンポーネントに動的なスタイルを設定

次はinputタグのツマミと同期して動的に変えたいスタイルを設定。

const P = styled.p`
     line-height: ${lineHeight}em;
   `;

   const Section = styled.section`
     max-width: ${lineLength}em;
   `;

   const H2 = styled.h2`
     && {
       font-size: ${jumpRate}em;
     }
   `;
  • Pコンポーネント はpタグでline-heightの値にlineHeightを埋め込んでいる
  • Sectionコンポーネント はsectionタグでmax-widthの値にlineLengthを埋め込んでいる
  • H2コンポーネント はh2タグでfont-sizeの値にjumpRateを埋め込んでいる

これらの設定はフックを埋め込みたいので関数コンポーネントの中に書いている。propsなどは使わず、Settingコンポーネントと同じ書き方で済んだ。


なお、当初H2のスタイルは外側のコンポーネントに設定しているスタイルの詳細度に負けてしまって設定が変わらなかった。こちらの&&を使う方法によってクラス名が二重になり、詳細度を上げることができた。

※参考:styled-componentsが反映されないときは詳細度を調べてみよう - りんごとバナナとエンジニア

Finally, the ampersand can be used to increase the specificity of rules on the component; this can be useful if you are dealing with a mixed styled-components and vanilla CSS environment where there might be conflicting styles:

最後に、アンパサンドを使用して、コンポーネントのルールの特異性を高めることができます。 これは、スタイルが競合する可能性のある、スタイル付きコンポーネントとバニラCSS環境が混在している場合に役立ちます。

※参考:styled-components: Basics


また、以前やったような副作用フックuseEffect()も不要だった。

※参考:【React】フック(React Hooks)事始め:useState、useEffect、useContext - クモのようにコツコツと

※参考:副作用フックの利用法 – React

useEffect()で囲ったところ、PSectionH2コンポーネントがスコープになってしまいJSXと連動されないエラー。

そしてuseEffect()がない状態でも問題なく動くようだった。


Exampleコンポーネントのスタイル設定が完成 f:id:idr_zz:20210406070428j:plain

この見た目の行長・行間・ジャンプ率がinputタグのツマミを変えることで動的に変わる想定。

ジャンプ率ジェネレーター完成(ブラウザ挙動)

ジャンプ率ジェネレーター完成!

※参考:React App

下記の「行長・行間・ジャンプ率」記事を体験できるジェネレーター。どのくらいの設定値が適切か調べることができる。

※参考:【行長・行間・ジャンプ率】タイポグラフィ事始め(適度な箱組みとは) - クモのようにコツコツと


初期設定はこちら(推奨値)

行長:35文字、行間:1.75倍、ジャンプ率:200% f:id:idr_zz:20210406071245j:plain


行長:35文字→47文字(本文の行長が広がる) f:id:idr_zz:20210406071249j:plain

行長:35文字→24文字(本文の行長が狭まる) f:id:idr_zz:20210406071253j:plain


行間:1.75倍→2.55倍(本文の行間が広がる) f:id:idr_zz:20210406071257j:plain

行間:1.75倍→1.4倍(本文の行間が狭まる) f:id:idr_zz:20210406071302j:plain


ジャンプ率:200%→320%(見出しのジャンプ率が上がる) f:id:idr_zz:20210406071306j:plain

ジャンプ率:200%→135%(見出しのジャンプ率が下がる) f:id:idr_zz:20210406071310j:plain


すべて変更してみる。

行長:43文字、行間:1.85倍、ジャンプ率:310% f:id:idr_zz:20210406071313j:plain


ソース(GitHub)

※参考:GitHub - ryo-i/jump-rate-generator at 0e5d7a565913d6ef50ea97bcd81127485084cbd5

プレビュー(GitHub Pages)

※参考:React App

最後に

ということで、ジャンプ率ジェネレーターが完成しましたーー!

styled-componentsの値を動的に変更する方法はpropsを使う情報が多かったのですが、今回は使わずに済みました。

なんとなくstyled-componentsは関数コンポーネントの外側に書くイメージがあったのですが、関数コンポーネントの中に書いて動的な設定をしている事例も見受けられたので安心しました。

これがベストプラクティスなのか確証が持てていないのですが、やりたいことは実現できたのでいったん一区切りとしたく思います(よりベターな方法があったら続報を書きます)。

それではまた!


※参考:ReactでWebアプリを作るシリーズまとめ
qiita.com