20210916のReactに関する記事は10件です。

【TypeScript】type派?interface派?僕はもちろんtype派

TypeScript? 型付けできるJavaScript let age: number = 0; age = 28; age = "ぞんぞん"; //エラー いろんな型 型 意味 例 string 文字列 'ぞんぞん' number 数値 28 boolean 真偽値 true [] 配列 string[] → ['aaa', 'bbb', 'ccc'] any 制約なし オブジェクトの型は? 普通に書くと読みづらい、管理しづらい?(ぱおん) const miyazon: {name: string, age: number, department: string} = { name: "みやぞん", age: 28, department: "product" } interface vs type(型エイリアス) オブジェクトの型指定にはinterfaceまたはtype(型エイリアス)を使う interface interface Employee { name: string; age: number; department: string; } const miyazon: Employee = { name: "みやぞん", age: 28, department: "product" } オブジェクトの型指定にはinterfaceまたはtype(型エイリアス)を使う type(型エイリアス) type Employee = { name: string; age: number; department: string; } const miyazon: Employee = { name: "みやぞん", age: 28, department: "product" } interfaceとtypeの違い interface type 用途 クラス、オブジェクトの型定義 型に別名をつける 交差型, 共用体型, タプル型, マップ型 非対応 対応 同じ要素名の再宣言 拡張される できない 「用途の違い」を感じる(interface) VS Codeなどで名前をホバーしたとき、型本来の情報を出してくれますが、ここで違いが出ます。 interfaceは型そのものなので名前が出ます。 「用途の違い」を感じる(type) 無名の型に別名を付けただけなので、本来の「無名の型」が表示されます。 「同じ要素名の再宣言」を感じる typeでは再宣言した次点でエラーとなるがinterfaceではどうなる...? interface Employee { name: string; } interface Employee { age: number; } interface Employee { department: string; } 「同じ要素名の再宣言」を感じる エラーにならない! interface Employee { name: string; } interface Employee { age: number; } interface Employee { department: string; } const miyzon: Employee = { name: "ぞんぞん", age: 28, department: "product" } どっちを使う? できることがtypeのほうが多い interfaceの再宣言による拡張でバグが発生しうる 型にはまらない人間でありたいが、私はtypeを選びます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

新技術は怖くない、jQuery時代からReact時代へ勇気を出して踏み出した

自分にとって未知の技術、なんだか怖い。 いずれ身に付けなければならないとは思いつつも、とても難しいものに思えてなかなか踏み出せなかったです。しかし考え方を変えてみたらあっさりとその壁を乗り越えて次の段階へと自分をステップアップさせることができました。 考え方の面から誰かの助けになればと思い、初投稿します。 この記事について JavaScriptについてjQuery全盛期に学習した後、技術的に停滞していた自分がついにReactに触れて個人アプリを作るようにまでなったので、その体験を簡潔に書きます。同じような境遇の人にとって新しい一歩を踏み出せる助けになると嬉しいです。 想定している読者は「ある技術を過去に習得したがレガシーになりつつある人」や「新しい技術を学んだ人が羨ましいが尻込みしてしまっている人」など、新しいことに焦燥感を抱えている人です。 jQuery時代に取り残された焦燥感 jQueryが悪いという意図はありません 私は社会人となる前の「JavaScriptのライブラリといえばjQuery」という時代にJavaScriptを学びました。それから社会人となりSES系の企業に就職しましたが、幸か不幸かそこは既に習得している技術だけでほとんどの業務をこなせるような技術水準の環境でした。 時折目にする統計では既にAngularやReactといった次世代のライブラリが主流になりつつありましたが、JavaScriptを触る機会があっても採用されているライブラリはjQuery1.8などで、周りを見渡しても同じような状態で誰も次世代ライブラリを触っていません。 QiitaなどでJavaScriptに関する記事のトレンドを見かけることがあってもjQueryが登場することはなく、「新入社員がReactで何かを作ってみた」というような記事が目に飛び込んできては眩しい思いがしていました。 いずれ今の自分の技術が通用しなくなることが薄々理解していながらも、ちょろっと見るだけでは全く理解できそうになく見えた次世代ライブラリを避けるように現状に甘んじていました。 Angular/React/Vueの中でAngularの人気が落ちてきた、という記事を見ては「Angularの時代を乗り切ったのだからjQueryで繋ぎつつ次の時代から飛び級してやるぜ」という謎のスマートフォンの機種変更のような心境でいました。 それでも本当にこれでいいのか、将来の自分のキャリアに満足できているだろうかという不安を抱えていました。余談ですがこういった焦燥感を抱くことを「クオーターライフ・クライシス」と呼ぶそうです。 新しい考え方がひらめき一気に変わった いささか乱暴です そんなモヤモヤを抱えながらシャワーをしていたある日(といっても最近です)、自分の中に新しい考え方が水と一緒に降ってきて、一気に新しい技術(React)への恐怖心がなくなりサクサクと技術習得が進んでいくようになりました。 経験のなかった新人にできて自分に出来ないわけがない 言葉は悪いですがこの考えに至りました、私は根拠もなく自信が持てる人間ですので。 よくよく考えてみればプログラミングの世界において、人気で主流となる新技術というのは原則として従来のものよりも便利で簡単に扱えるからです。従来のモノ(jQuery)よりも便利で簡単な新しいモノ(React)が登場したから時代が変わったのです。 であればjQueryが扱えるようになった自分が、より楽に色々できるReactを扱えないはずがありません。自分より若く経験も浅いどころか、未経験だった新社会人が次々と習得できているのですから。 実際、Reactで何個か個人アプリを作ってみましたが完全に理解しました。 ⇒もしかして:ダニング=クルーガー効果 考え方に関してはここまで、いったんまとめ 要するに、同じような境遇の人に伝えたいことは以下です。 主流となる新技術は従来よりも便利で簡単だからこそ主流となった。だから従来のものを習得できたあなたに新しいものが習得できないはずがない。 未経験だった人でも習得して仕事に出来ています。 ここまで読んでくれた方、ありがとうございました、よければ最後まで。 次は簡潔にjQuery時代からReact時代にステップアップした手順を書いていきます。 jQueryからReactに行くためにやったこと Reactのチュートリアルを愚直にやる Next.jsでウェブサイトを作る Next.jsのブログテーマを作る npmへReactコンポーネントの公開もした 以上です。勉強するライブラリは悩みましたがReactが人気のようだったのでReactを勉強することに決めました。そしてせっかく勉強したのですからReactを使った作品を作ろうと思い、ReactをベースとしたフレームワークNext.jsを使用してアプリを作り実践としました。 React公式チュートリアル Reactの公式チュートリアルは本当に優秀なチュートリアルです。 これまでの私はQiitaなどの入門記事を読んでは難しそう、で引き返していましたが大本であるReact公式のチュートリアルへアクセスし、そこに書かれている通りに簡単なゲームを作りました。手取り足取り説明が書かれているのでやることが明確にわかりました。 (実はこの時、英語表記でGoogle翻訳を有効にしながらやっていたのですが、公式に日本語表示があったようなので今からやる人は日本語表示が良いと思います) なんとなくReactはコンポーネントという部品の組み合わせで表現する仕組みで、不変な引数Propsと可変な状態を表すStateであることが分かるようになりました。Stateの相互作用についてもドキュメントが非常に理解の助けになりました。 Next.jsでウェブサイトを作った Reactで何か作りたいと思い探したところ、Next.jsというフレームワークが手軽に自分のウェブサイトをReactで作成して公開することが出来ることが分かりました。 私は趣味でアメリカ株への投資を行っているので、決算情報をグラフィカルに表示することのできるウェブサイトを作ってみることにしました。Next.jsのチュートリアルをやった後、作ってみました。このチュートリアルも非常に分かりやすかったです。 これでなんとなくコンポーネントの作り方が分かってきました。Atomic Designというデザインパターンにも出会いましたが、よく細分化の粒度が理解できず粗削りな状態ですが一旦形になったので満足しています。 Next.jsのブログテーマを作った 実は個人ブログを Infoseek ⇒ livedoor ⇒ WordPress ⇒ Hugo と渡り歩いてきた私です。いつもカスタマイズをせずにいられなかった私は身に付けたばかりのReactでオリジナルデザインなブログを作ることにしたく時を開けずにNext.jsでブログテーマを作ることにしました。 Reactコンポーネントをnpmパッケージとして公開した せっかくなのでブログテーマを作る途上で作成したブログパーツをnpmパッケージとしても公開してみました。これも初めての体験でした。 まとめ このように出来るはずだと思い立ち、チュートリアルで学んだことを何かしら自作アプリを作ってみることで、ある程度新しい技術を定着させることができたように思います。 学んでいる最中は久しく忘れていた未知のものに触れて扱えるようになっていくワクワク感がありました。(初めてHTMLにスタイルを付けて、文字色を変更できた時のことを思い出しました) まだまだReactのstateのリフトアップやHookについての理解度が足りないという事が課題と感じていますが、明らかにjQueryしか扱えなかった頃の自分よりスキルアップすることができているのでモチベーションも高くなりました。(同じロジックでTypeScriptやWebPackの学習も始めました) ぜひ同じように新しいことへの挑戦に尻込みしている人がいれば、新しい技術は怖くないよ、ということで一歩踏み出してみる助けになれたらと思います。読んでいただきありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

基礎から学ぶReact/React Hooks学習メモ 5-6 カスタムフック

React Hooksを基礎から理解する 5-6 カスタムフック 参考 基礎から学ぶReact/React Hooks カスタムフックとは 関数名がuseから始まる独自のフック。ほかのフックを呼び出せる関数。 UIと切り離してロジックごとに処理を切り出すことにより、カスタムフックの使いまわしや、重複したロジックを取り除くことができる。 カスタムフックの作成例 ベースとなるコンポーネント import React, { useState } from "react"; const CounterText = (props) => <p>現在のカウント数: {props.count}</p>; const INITIAL_COUNT = 0; const Counter = () => { const [count, setCount] = useState(INITIAL_COUNT); const countAdd = () => setCount((prevCount) => prevCount + 1); const countSub = () => setCount((prevCount) => prevCount - 1); const countReset = () => setCount(INITIAL_COUNT); return ( <> <CounterText count={count} /> <button onClick={countAdd}>ボタン +1</button> <button onClick={countSub}>ボタン -1</button> <button onClick={countReset}>リセット</button> </> ); }; export default function App() { return <Counter />; } ロジックをフックに切り出す import React, { useState } from "react"; const CounterText = (props) => <p>現在のカウント数: {props.count}</p>; const INITIAL_COUNT = 0; // フックを定義 const useCounter = (initialCount) => { const [count, setCount] = useState(initialCount); const countAdd = () => setCount((prevCount) => prevCount + 1); const countSub = () => setCount((prevCount) => prevCount - 1); const countReset = () => setCount(initialCount); return { count, countAdd, countSub, countReset }; }; const Counter = () => { // カスタムフックの利用 const { count, countAdd, countSub, countReset } = useCounter(INITIAL_COUNT); return ( <> <CounterText count={count} /> <button onClick={countAdd}>ボタン +1</button> <button onClick={countSub}>ボタン -1</button> <button onClick={countReset}>リセット</button> </> ); }; export default function App() { return <Counter />; } カスタムフックを利用する カウンターカスタムフック useCounter.js import React, { useState } from "react"; export const useCounter = (initialValue) => { const [count, setCount] = useState(initialValue); const countAdd = () => setCount((prevCount) => prevCount + 1); const countSub = () => setCount((prevCount) => prevCount - 1); const countReset = () => setCount(initialValue); return { count, countAdd, countSub, countReset }; }; 「現在のカウント数」表示コンポーネント CounterText.js import React from "react"; export const CounterText = ({ count }) => { return ( <p> 現在のカウント数: <b>{count}</b> </p> ); }; カウンター部分(タイトル、ボタン) Counter.js import React from "react"; import { useCounter } from "./useCounter"; import { CounterText } from "./CounterText"; export const Counter = ({ counterName }) => { const { count, countAdd, countSub, countReset } = useCounter(0); return ( <p> <h1>{counterName}</h1> <CounterText count={count} /> <button onClick={countAdd}>ボタン +1</button> <button onClick={countSub}>ボタン -1</button> <button onClick={countReset}>リセット</button> </p> ); }; 全体 App.js import { Counter } from "./Counter"; import "./styles.css"; export default function App() { return ( <div> <Counter counterName="カウンターA" /> <Counter counterName="カウンターB" /> <Counter counterName="カウンターC" /> </div> ); }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React Reduxの全体像とデータフロー

はじめに ずっとReduxから逃げて他のstate管理ライブラリを使っていたんですが、ついに捕まったので調査しました。 Reduxの全体像 要素 役割 Component Storeを利用するコンポーネント。ComponentはUIにのみ責任を持ち、ロジックはContainerに分離する。 Action Storeに対して、stateの参照や変更を依頼するための依頼書。ComponentがActionCreatorを通して作成する。 ActionCreator Componentから必要な情報を受け取り、Actionを作成する Container ComponentとStoreを接続し、Componentに対してstateとdispatch()を提供する。stateに対して何がしかの処理を加えたい場合、Container内にロジックを記述する。 dispatch() actionをReducerに届け、処理を促す。 Store データストア。stateを集中管理する。Reducer以外からのstate変更を受け付けない。 Reducer Actionを受け取り、Store内のstateに変更を及ぼす。stateの種類によってReducerを分けるのが一般的。 CombineReducer 複数あるReducerをひとまとめにし、Storeと接続する。 ユーザアクションに応じて、Store内のstateを更新する際のデータフロー 下記流れでStore内のstateを更新します。 Componentがイベントを検知し、Actionを作成する。 作成したActionを、Containerから提供されたdispatch()を用いて、Reducerに処理を依頼する。 Reducerは、現在のstateとActionをもとに新たなstateを計算し、そのstateをStoreに反映させる。 Componentがイベントを検知し、Actionを作成する。 Componentはイベント(ユーザのクリックとか)を検知して、ActionCreatorを通じてActionを作成します。 この時、ComponentはActionCreatorに対して、下記の情報を提供します。 Type アクションの種類を識別するための文字列 Payload アクションに必要なデータ(引数) ActionCreatorは上記をもとに、Actionを作成します。 Actionの例↓ { type: "todos/todoAdded", payload: todoText, } 作成したActionを、Containerから提供されたdispatch()を用いて、Reducerに処理を依頼する。 ComponentはActionをDispatchして、Reducerに処理を依頼する。 index.js dispatch({ type: 'todos/todoAdded', payload: 'todoText' }) Reducerの中身はこんな感じ↓ todosReducer.js // Use the initialState as a default value export default function appReducer(state = initialState, action) { // The reducer normally looks at the action type field to decide what happens switch (action.type) { // Do something here based on the different types of actions case 'todos/todoAdded': { // We need to return a new state object return { // that has all the existing state data ...state, // but has a new array for the `todos` field todos: [ // with all of the old todos ...state.todos, // and the new todo object { // Use an auto-incrementing numeric ID for this example id: nextTodoId(state.todos), text: action.payload, completed: false } ] } } default: // If this reducer doesn't recognize the action type, or doesn't // care about this specific action, return the existing state unchanged return state } } ActionのTypeを参照して、処理を分岐させてる。 また、Action.payloadを参照することで、Componentが引数として指定した任意のデータも参照することができる。 Reducerは、現在のstateとActionをもとに新たなstateを計算し、そのstateをStoreに反映させる。 実際に新たなstateを反映しているのは、下記のreturn文。 todosReducer.js case 'todos/todoAdded': { // We need to return a new state object return { // that has all the existing state data ...state, // but has a new array for the `todos` field todos: [ // with all of the old todos ...state.todos, // and the new todo object { // Use an auto-incrementing numeric ID for this example id: nextTodoId(state.todos), text: action.payload, completed: false } ] 補足)ContainerとComponent Containerの役割はStoreとComponentの橋渡し。 しかし、ComponentはContainerがいなくても、直接Storeのdispatch()を利用することができる。 なぜContainerが必要か? ComponentがReduxに依存してしまう。 ⇒ Componentが状態やロジックを保持することになり、テストがしにくくなる。 Redux以外のデータソースから、Componentに対して値を受け渡しにくくなる。 ちなみに、Containerは下記のように、Componentに対してStateとdispatch()を提供する。 container.js function mapStateToProps (state) { return {...state}; } function mapDispatchToProps(dispatch) { return { todoAdded: () => dispatch(todoAdded()), }; } export default connect(mapStateToProps, mapDispatchToProps)(Component); ※上記のconnect()は古いらしく、hook全盛期のReactではもっとシンプルに書けるそうです。 https://qiita.com/seya/items/700184c0d4a52bc0d32b 間違いあればご指摘ください! 追記 Container内で、dispatch()をカスタマイズ(任意のActionを定義)する関数を作成し、Componentはその関数に対してPayloadを投げてあげる。みたいな構成もあるらしい。 (そっちの方がAction単位でテストできるし、Containerの肥大化も避けれるしよさげ??) 参考 https://redux.js.org/tutorials/essentials/part-1-overview-concepts https://qiita.com/mpyw/items/a816c6380219b1d5a3bf#action-%E3%81%8A%E3%82%88%E3%81%B3-action-creator https://qiita.com/seya/items/700184c0d4a52bc0d32b https://react-redux.js.org/api/hooks
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TypeScript よく使う型の超簡易的な書き方の紹介 *初心者用

TypeScriptのすごくざっくりとした 型の書き方の紹介です。 詳しいことなどは載せていないので 間違いなどがありましたらFB頂けると幸いです。 メモ程度の感覚で見て頂けると幸いです。 要素1つ1つの型 sample.ts boolean :boolean number :number string :string etc... 配列の型 sample.ts Array<要素1つ1つの型> 例) Array<number> = [1, 2, 3] オブジェクトの型 sample.ts object = { 要素1つ1つの型 }; 例 object = { number: number }; 以上
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React】SVG の Path上を移動する軌道アニメーション

概要 SVGのpath上を移動するアニメーションについて、実装例をふまえて調べたことをまとめました。 実装 べた書き + 説明 import React, { VFC } from 'react'; import { css } from '@emotion/css'; export const SVGPathAnimation: VFC = () => { return ( <div className={styles.container}> <svg viewBox="0 0 193 193" fill="none" xmlns="http://www.w3.org/2000/svg"> <g id="react-icon" transform="translate(6.5 0)"> <path id="circle" d="M90 80.725C98.8769 80.725 106.073 87.9074 106.073 96.7671C106.073 105.627 98.8769 112.809 90 112.809C81.123 112.809 73.9266 105.627 73.9266 96.7671C73.9266 87.9074 81.123 80.725 90 80.725Z" fill="#00D8FF" /> <path id="ring1" d="M139.831 69.9227C141.693 70.4192 143.541 70.9592 145.373 71.542C163.966 77.4769 175.982 86.7733 175.982 96.3865C175.982 106.411 163.141 116.213 143.405 122.276C142.363 122.597 141.293 122.905 140.204 123.204L133.09 124.933C125.959 126.461 118.097 127.581 109.774 128.266L100.296 128.847C96.8819 128.988 93.4132 129.061 89.9041 129.061C86.4886 129.061 83.1282 129 79.8317 128.88L70.2911 128.353C61.949 127.725 54.1434 126.685 47.0917 125.239L39.9607 123.568C38.3974 123.154 36.8455 122.704 35.3064 122.218C16.3222 116.202 3.82609 106.279 3.82609 96.3865C3.82609 86.8082 15.4661 77.5742 33.6932 71.6817C35.7301 71.0232 37.8585 70.4047 40.0643 69.8235L47.1933 68.1443C54.3189 66.6472 62.0693 65.5298 70.166 64.8339L79.7705 64.2154C86.5492 63.921 93.3392 63.9223 100.118 64.2191L109.692 64.8426C117.812 65.5491 125.568 66.6858 132.711 68.2148L139.831 69.9227Z" stroke="#00D8FF" strokeWidth="1" /> <path id="ring2" d="M91.8267 39.9871C93.1875 38.6233 94.5789 37.2932 95.9997 35.9979C110.436 22.8629 124.495 17.105 132.82 21.9116C141.502 26.924 143.57 42.9457 138.953 63.069C138.709 64.132 138.441 65.212 138.156 66.3051L136.096 73.3308C133.855 80.27 130.893 87.6392 127.325 95.1892L123.089 103.688C121.504 106.715 119.833 109.756 118.078 112.795C116.37 115.753 114.637 118.632 112.885 121.427L107.658 129.426C102.944 136.337 98.1405 142.576 93.3618 147.96L88.3496 153.3C87.2094 154.447 86.0436 155.566 84.8534 156.656C70.1508 170.089 55.3096 175.949 46.7424 171.003C38.4474 166.214 36.2705 151.516 40.2809 132.785C40.7292 130.692 41.2577 128.539 41.8573 126.338L43.9675 119.325C46.2338 112.405 49.1413 105.135 52.587 97.7748L56.8536 89.1478C59.988 83.13 63.3841 77.2504 67.0305 71.5283L72.3576 63.5486C77.0295 56.8695 81.8918 50.7212 86.7875 45.2995L91.8267 39.9871Z" stroke="#00D8FF" strokeWidth="1" /> <path id="ring3" d="M41.8994 66.5927C41.3987 64.7323 40.9425 62.8622 40.5312 60.9841C36.3742 41.9142 38.4171 26.8599 46.7424 22.0533C55.4241 17.0409 70.3335 23.2606 85.452 37.3211C86.2508 38.0636 87.0522 38.8356 87.8561 39.6294L92.9106 44.9259C97.7994 50.3369 102.701 56.5862 107.455 63.4515L112.697 71.3691C114.526 74.2555 116.324 77.223 118.078 80.2619C119.786 83.2198 121.413 86.1606 122.958 89.0753L127.271 97.6014C130.899 105.14 133.901 112.419 136.174 119.25L138.293 126.261C138.716 127.821 139.102 129.39 139.451 130.966C143.733 150.415 141.388 166.199 132.82 171.145C124.525 175.934 110.708 170.47 96.4919 157.632C94.9032 156.197 93.3033 154.663 91.6971 153.043L86.6784 147.709C81.819 142.286 76.9761 136.133 72.3251 129.469L66.9872 121.461C63.3429 115.737 59.949 109.856 56.8168 103.837L52.5697 95.2342C49.1214 87.8486 46.228 80.5636 43.9805 73.613L41.8994 66.5927Z" stroke="#00D8FF" strokeWidth="1" /> {/* ring1 軌道を移動するCircle */} <circle r="5" fill="#00D8FF"> <animateMotion dur="5s" repeatCount="indefinite"> <mpath xlinkHref="#ring1" /> </animateMotion> </circle> {/* ring2 軌道を移動するCircle */} <circle r="5" fill="#00D8FF"> <animateMotion dur="7s" repeatCount="indefinite" keyPoints="1;0" keyTimes="0;1" calcMode="linear"> <mpath xlinkHref="#ring2" /> </animateMotion> </circle> {/* ring3 軌道を移動するCircle */} <circle r="5" fill="#00D8FF"> <animateMotion dur="10s" repeatCount="indefinite" keyPoints="1;0" keyTimes="0;1" calcMode="linear"> <mpath xlinkHref="#ring3" /> </animateMotion> </circle> </g> </svg> </div> ) } path上を移動するアニメーションを実装するためには、animateMotionタグを使用します。 pathタグで軌道を定義している場合は、mpathタグを使用することでpathを参照することができます。 逆方向に移動させる場合は、animateMotionタグに以下のプロパティを追加します。 keyPoints="1;0" keyTimes="0;1" calcMode="linear" コンポーネント化 もう少し綺麗に書くと、 import React, { VFC } from 'react'; import { css } from '@emotion/css'; export const SVGPathAnimation: VFC = () => { return ( <div className={styles.container}> <svg viewBox="0 0 193 193" fill="none" xmlns="http://www.w3.org/2000/svg"> <g id="react-icon" transform="translate(6.5 0)"> <path id="circle" d="M90 80.725C98.8769 80.725 106.073 87.9074 106.073 96.7671C106.073 105.627 98.8769 112.809 90 112.809C81.123 112.809 73.9266 105.627 73.9266 96.7671C73.9266 87.9074 81.123 80.725 90 80.725Z" fill="#00D8FF" /> <path id="ring1" d="M139.831 69.9227C141.693 70.4192 143.541 70.9592 145.373 71.542C163.966 77.4769 175.982 86.7733 175.982 96.3865C175.982 106.411 163.141 116.213 143.405 122.276C142.363 122.597 141.293 122.905 140.204 123.204L133.09 124.933C125.959 126.461 118.097 127.581 109.774 128.266L100.296 128.847C96.8819 128.988 93.4132 129.061 89.9041 129.061C86.4886 129.061 83.1282 129 79.8317 128.88L70.2911 128.353C61.949 127.725 54.1434 126.685 47.0917 125.239L39.9607 123.568C38.3974 123.154 36.8455 122.704 35.3064 122.218C16.3222 116.202 3.82609 106.279 3.82609 96.3865C3.82609 86.8082 15.4661 77.5742 33.6932 71.6817C35.7301 71.0232 37.8585 70.4047 40.0643 69.8235L47.1933 68.1443C54.3189 66.6472 62.0693 65.5298 70.166 64.8339L79.7705 64.2154C86.5492 63.921 93.3392 63.9223 100.118 64.2191L109.692 64.8426C117.812 65.5491 125.568 66.6858 132.711 68.2148L139.831 69.9227Z" stroke="#00D8FF" strokeWidth="1" /> <path id="ring2" d="M91.8267 39.9871C93.1875 38.6233 94.5789 37.2932 95.9997 35.9979C110.436 22.8629 124.495 17.105 132.82 21.9116C141.502 26.924 143.57 42.9457 138.953 63.069C138.709 64.132 138.441 65.212 138.156 66.3051L136.096 73.3308C133.855 80.27 130.893 87.6392 127.325 95.1892L123.089 103.688C121.504 106.715 119.833 109.756 118.078 112.795C116.37 115.753 114.637 118.632 112.885 121.427L107.658 129.426C102.944 136.337 98.1405 142.576 93.3618 147.96L88.3496 153.3C87.2094 154.447 86.0436 155.566 84.8534 156.656C70.1508 170.089 55.3096 175.949 46.7424 171.003C38.4474 166.214 36.2705 151.516 40.2809 132.785C40.7292 130.692 41.2577 128.539 41.8573 126.338L43.9675 119.325C46.2338 112.405 49.1413 105.135 52.587 97.7748L56.8536 89.1478C59.988 83.13 63.3841 77.2504 67.0305 71.5283L72.3576 63.5486C77.0295 56.8695 81.8918 50.7212 86.7875 45.2995L91.8267 39.9871Z" stroke="#00D8FF" strokeWidth="1" /> <path id="ring3" d="M41.8994 66.5927C41.3987 64.7323 40.9425 62.8622 40.5312 60.9841C36.3742 41.9142 38.4171 26.8599 46.7424 22.0533C55.4241 17.0409 70.3335 23.2606 85.452 37.3211C86.2508 38.0636 87.0522 38.8356 87.8561 39.6294L92.9106 44.9259C97.7994 50.3369 102.701 56.5862 107.455 63.4515L112.697 71.3691C114.526 74.2555 116.324 77.223 118.078 80.2619C119.786 83.2198 121.413 86.1606 122.958 89.0753L127.271 97.6014C130.899 105.14 133.901 112.419 136.174 119.25L138.293 126.261C138.716 127.821 139.102 129.39 139.451 130.966C143.733 150.415 141.388 166.199 132.82 171.145C124.525 175.934 110.708 170.47 96.4919 157.632C94.9032 156.197 93.3033 154.663 91.6971 153.043L86.6784 147.709C81.819 142.286 76.9761 136.133 72.3251 129.469L66.9872 121.461C63.3429 115.737 59.949 109.856 56.8168 103.837L52.5697 95.2342C49.1214 87.8486 46.228 80.5636 43.9805 73.613L41.8994 66.5927Z" stroke="#00D8FF" strokeWidth="1" /> {/* 軌道アニメーション */} <AnimationCircle pathId="ring1" durSec={5} /> <AnimationCircle pathId="ring2" durSec={7} isReverse /> <AnimationCircle pathId="ring3" durSec={10} isReverse /> </g> </svg> </div> ) } type AnimationCircleProps = { pathId: string durSec?: number isReverse?: boolean } const AnimationCircle: VFC<AnimationCircleProps> = props => { const { pathId, durSec = 5, isReverse = false } = props return ( <circle r="5" fill="#00D8FF"> <animateMotion dur={`${durSec}s`} repeatCount="indefinite" keyPoints={isReverse ? '1;0' : '0;1'} keyTimes="0;1" calcMode="linear"> <mpath xlinkHref={`#${pathId}`} /> </animateMotion> </circle> ) } const styles = { container: css` display: flex; justify-content: center; align-items: center; width: 500px; height: 500px; ` } まとめ CSSやJSを使わないでも、基本的なアニメーションならSVGだけで実装できることを知りました。 おまけ SVG 作成ツール Figma が便利です。 ブラウザ上で動作するのでインストールする必要がないですし、アカウント登録すればFreeで使用できます。 また、プラグインとしてiconifyを導入すると、色々なアイコンのSVGを使うことができます。 プラグインの導入方法 アイコンの使用方法 参照
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React】SVGのPath上を移動する軌道アニメーション

概要 SVGのpath上を移動するアニメーションについて、実装例をふまえて調べたことをまとめました。 実装 べた書き + 説明 import React, { VFC } from 'react'; import { css } from '@emotion/css'; export const SVGPathAnimation: VFC = () => { return ( <div className={styles.container}> <svg viewBox="0 0 193 193" fill="none" xmlns="http://www.w3.org/2000/svg"> <g id="react-icon" transform="translate(6.5 0)"> <path id="circle" d="M90 80.725C98.8769 80.725 106.073 87.9074 106.073 96.7671C106.073 105.627 98.8769 112.809 90 112.809C81.123 112.809 73.9266 105.627 73.9266 96.7671C73.9266 87.9074 81.123 80.725 90 80.725Z" fill="#00D8FF" /> <path id="ring1" d="M139.831 69.9227C141.693 70.4192 143.541 70.9592 145.373 71.542C163.966 77.4769 175.982 86.7733 175.982 96.3865C175.982 106.411 163.141 116.213 143.405 122.276C142.363 122.597 141.293 122.905 140.204 123.204L133.09 124.933C125.959 126.461 118.097 127.581 109.774 128.266L100.296 128.847C96.8819 128.988 93.4132 129.061 89.9041 129.061C86.4886 129.061 83.1282 129 79.8317 128.88L70.2911 128.353C61.949 127.725 54.1434 126.685 47.0917 125.239L39.9607 123.568C38.3974 123.154 36.8455 122.704 35.3064 122.218C16.3222 116.202 3.82609 106.279 3.82609 96.3865C3.82609 86.8082 15.4661 77.5742 33.6932 71.6817C35.7301 71.0232 37.8585 70.4047 40.0643 69.8235L47.1933 68.1443C54.3189 66.6472 62.0693 65.5298 70.166 64.8339L79.7705 64.2154C86.5492 63.921 93.3392 63.9223 100.118 64.2191L109.692 64.8426C117.812 65.5491 125.568 66.6858 132.711 68.2148L139.831 69.9227Z" stroke="#00D8FF" strokeWidth="1" /> <path id="ring2" d="M91.8267 39.9871C93.1875 38.6233 94.5789 37.2932 95.9997 35.9979C110.436 22.8629 124.495 17.105 132.82 21.9116C141.502 26.924 143.57 42.9457 138.953 63.069C138.709 64.132 138.441 65.212 138.156 66.3051L136.096 73.3308C133.855 80.27 130.893 87.6392 127.325 95.1892L123.089 103.688C121.504 106.715 119.833 109.756 118.078 112.795C116.37 115.753 114.637 118.632 112.885 121.427L107.658 129.426C102.944 136.337 98.1405 142.576 93.3618 147.96L88.3496 153.3C87.2094 154.447 86.0436 155.566 84.8534 156.656C70.1508 170.089 55.3096 175.949 46.7424 171.003C38.4474 166.214 36.2705 151.516 40.2809 132.785C40.7292 130.692 41.2577 128.539 41.8573 126.338L43.9675 119.325C46.2338 112.405 49.1413 105.135 52.587 97.7748L56.8536 89.1478C59.988 83.13 63.3841 77.2504 67.0305 71.5283L72.3576 63.5486C77.0295 56.8695 81.8918 50.7212 86.7875 45.2995L91.8267 39.9871Z" stroke="#00D8FF" strokeWidth="1" /> <path id="ring3" d="M41.8994 66.5927C41.3987 64.7323 40.9425 62.8622 40.5312 60.9841C36.3742 41.9142 38.4171 26.8599 46.7424 22.0533C55.4241 17.0409 70.3335 23.2606 85.452 37.3211C86.2508 38.0636 87.0522 38.8356 87.8561 39.6294L92.9106 44.9259C97.7994 50.3369 102.701 56.5862 107.455 63.4515L112.697 71.3691C114.526 74.2555 116.324 77.223 118.078 80.2619C119.786 83.2198 121.413 86.1606 122.958 89.0753L127.271 97.6014C130.899 105.14 133.901 112.419 136.174 119.25L138.293 126.261C138.716 127.821 139.102 129.39 139.451 130.966C143.733 150.415 141.388 166.199 132.82 171.145C124.525 175.934 110.708 170.47 96.4919 157.632C94.9032 156.197 93.3033 154.663 91.6971 153.043L86.6784 147.709C81.819 142.286 76.9761 136.133 72.3251 129.469L66.9872 121.461C63.3429 115.737 59.949 109.856 56.8168 103.837L52.5697 95.2342C49.1214 87.8486 46.228 80.5636 43.9805 73.613L41.8994 66.5927Z" stroke="#00D8FF" strokeWidth="1" /> {/* ring1 軌道を移動するCircle */} <circle r="5" fill="#00D8FF"> <animateMotion dur="5s" repeatCount="indefinite"> <mpath xlinkHref="#ring1" /> </animateMotion> </circle> {/* ring2 軌道を移動するCircle */} <circle r="5" fill="#00D8FF"> <animateMotion dur="7s" repeatCount="indefinite" keyPoints="1;0" keyTimes="0;1" calcMode="linear"> <mpath xlinkHref="#ring2" /> </animateMotion> </circle> {/* ring3 軌道を移動するCircle */} <circle r="5" fill="#00D8FF"> <animateMotion dur="10s" repeatCount="indefinite" keyPoints="1;0" keyTimes="0;1" calcMode="linear"> <mpath xlinkHref="#ring3" /> </animateMotion> </circle> </g> </svg> </div> ) } path上を移動するアニメーションを実装するためには、animateMotionタグを使用します。 pathタグで軌道を定義している場合は、mpathタグを使用することでpathを参照することができます。 逆方向に移動させる場合は、animateMotionタグに以下のプロパティを追加します。 keyPoints="1;0" keyTimes="0;1" calcMode="linear" コンポーネント化 もう少し綺麗に書くと、 import React, { VFC } from 'react'; import { css } from '@emotion/css'; export const SVGPathAnimation: VFC = () => { return ( <div className={styles.container}> <svg viewBox="0 0 193 193" fill="none" xmlns="http://www.w3.org/2000/svg"> <g id="react-icon" transform="translate(6.5 0)"> <path id="circle" d="M90 80.725C98.8769 80.725 106.073 87.9074 106.073 96.7671C106.073 105.627 98.8769 112.809 90 112.809C81.123 112.809 73.9266 105.627 73.9266 96.7671C73.9266 87.9074 81.123 80.725 90 80.725Z" fill="#00D8FF" /> <path id="ring1" d="M139.831 69.9227C141.693 70.4192 143.541 70.9592 145.373 71.542C163.966 77.4769 175.982 86.7733 175.982 96.3865C175.982 106.411 163.141 116.213 143.405 122.276C142.363 122.597 141.293 122.905 140.204 123.204L133.09 124.933C125.959 126.461 118.097 127.581 109.774 128.266L100.296 128.847C96.8819 128.988 93.4132 129.061 89.9041 129.061C86.4886 129.061 83.1282 129 79.8317 128.88L70.2911 128.353C61.949 127.725 54.1434 126.685 47.0917 125.239L39.9607 123.568C38.3974 123.154 36.8455 122.704 35.3064 122.218C16.3222 116.202 3.82609 106.279 3.82609 96.3865C3.82609 86.8082 15.4661 77.5742 33.6932 71.6817C35.7301 71.0232 37.8585 70.4047 40.0643 69.8235L47.1933 68.1443C54.3189 66.6472 62.0693 65.5298 70.166 64.8339L79.7705 64.2154C86.5492 63.921 93.3392 63.9223 100.118 64.2191L109.692 64.8426C117.812 65.5491 125.568 66.6858 132.711 68.2148L139.831 69.9227Z" stroke="#00D8FF" strokeWidth="1" /> <path id="ring2" d="M91.8267 39.9871C93.1875 38.6233 94.5789 37.2932 95.9997 35.9979C110.436 22.8629 124.495 17.105 132.82 21.9116C141.502 26.924 143.57 42.9457 138.953 63.069C138.709 64.132 138.441 65.212 138.156 66.3051L136.096 73.3308C133.855 80.27 130.893 87.6392 127.325 95.1892L123.089 103.688C121.504 106.715 119.833 109.756 118.078 112.795C116.37 115.753 114.637 118.632 112.885 121.427L107.658 129.426C102.944 136.337 98.1405 142.576 93.3618 147.96L88.3496 153.3C87.2094 154.447 86.0436 155.566 84.8534 156.656C70.1508 170.089 55.3096 175.949 46.7424 171.003C38.4474 166.214 36.2705 151.516 40.2809 132.785C40.7292 130.692 41.2577 128.539 41.8573 126.338L43.9675 119.325C46.2338 112.405 49.1413 105.135 52.587 97.7748L56.8536 89.1478C59.988 83.13 63.3841 77.2504 67.0305 71.5283L72.3576 63.5486C77.0295 56.8695 81.8918 50.7212 86.7875 45.2995L91.8267 39.9871Z" stroke="#00D8FF" strokeWidth="1" /> <path id="ring3" d="M41.8994 66.5927C41.3987 64.7323 40.9425 62.8622 40.5312 60.9841C36.3742 41.9142 38.4171 26.8599 46.7424 22.0533C55.4241 17.0409 70.3335 23.2606 85.452 37.3211C86.2508 38.0636 87.0522 38.8356 87.8561 39.6294L92.9106 44.9259C97.7994 50.3369 102.701 56.5862 107.455 63.4515L112.697 71.3691C114.526 74.2555 116.324 77.223 118.078 80.2619C119.786 83.2198 121.413 86.1606 122.958 89.0753L127.271 97.6014C130.899 105.14 133.901 112.419 136.174 119.25L138.293 126.261C138.716 127.821 139.102 129.39 139.451 130.966C143.733 150.415 141.388 166.199 132.82 171.145C124.525 175.934 110.708 170.47 96.4919 157.632C94.9032 156.197 93.3033 154.663 91.6971 153.043L86.6784 147.709C81.819 142.286 76.9761 136.133 72.3251 129.469L66.9872 121.461C63.3429 115.737 59.949 109.856 56.8168 103.837L52.5697 95.2342C49.1214 87.8486 46.228 80.5636 43.9805 73.613L41.8994 66.5927Z" stroke="#00D8FF" strokeWidth="1" /> {/* 軌道アニメーション */} <AnimationCircle pathId="ring1" durSec={5} /> <AnimationCircle pathId="ring2" durSec={7} isReverse /> <AnimationCircle pathId="ring3" durSec={10} isReverse /> </g> </svg> </div> ) } type AnimationCircleProps = { pathId: string durSec?: number isReverse?: boolean } const AnimationCircle: VFC<AnimationCircleProps> = props => { const { pathId, durSec = 5, isReverse = false } = props return ( <circle r="5" fill="#00D8FF"> <animateMotion dur={`${durSec}s`} repeatCount="indefinite" keyPoints={isReverse ? '1;0' : '0;1'} keyTimes="0;1" calcMode="linear"> <mpath xlinkHref={`#${pathId}`} /> </animateMotion> </circle> ) } const styles = { container: css` display: flex; justify-content: center; align-items: center; width: 500px; height: 500px; ` } まとめ CSSやJSを使わないでも、基本的なアニメーションならSVGだけで実装できることを知りました。 おまけ SVG 活用例 SVG 作成ツール Figma が便利です。 ブラウザ上で動作するのでインストールする必要がないですし、アカウント登録すればFreeで使用できます。 プラグインとして iconify を導入すると、色々なアイコンのSVGを使うことができます。 プラグインの導入方法 アイコンの使用方法 参照
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

基礎から学ぶReact/React Hooks学習メモ 5-5 useContext

React Hooksを基礎から理解する 5-5 useContext 参考 基礎から学ぶReact/React Hooks useContextとは コンポーネントのさまざまな階層からContextに収容されているデータへのアクセスをすることができる。 親から子、孫へ、propsのいわゆるバケツリレーをしなくても直接値を渡せる。 Contextオブジェクト React.createContext()の戻り値 const SampleContextObject = React.createContext(); Providerコンポーネント Contextオブジェクトが保持しているコンポーネント 1つのContextで管理できる「コンテキストオブジェクトの値」は1つだけ。 ConsumerComponentコンポーネントはProvider内なので、value属性の値を参照できる。 <SampleContextObject.Provider value={ツリー内で共有可能なコンテキストオブジェクトの値} > <ConsumerComponent /> </SampleContextObject.Provider> Consumerコンポーネント useContext()を利用することで、Contextから値を取得できるコンポーネント import React, { useContext } from "react"; const コンテキストオブジェクトの値 = useContext(コンテキストオブジェクト); useContextの利用例 Providerコンポーネントで共有されたデータをConsumerコンポーネントで受け取る import React, { createContext, useContext } from "react"; import "./styles.css"; // contextオブジェクトの生成 const SampleContextObject = createContext(); const ConsumerComponent = () => { // 親コンポーネントからmessageTextを取得 const messageText = useContext(SampleContextObject); console.log(messageText); return <p>{messageText}</p>; }; const message = "I love React!!"; export default function App() { // 親コンポーネントに文字列を設定して、Providerでラップする return ( <SampleContextObject.Provider value={message}> <ConsumerComponent /> </SampleContextObject.Provider> ); } Providerコンポーネントからテキストデータを渡してみる TextProvider.js import React, { createContext } from "react"; export const TextContext = createContext(); const text = "これはProviderから渡されたテキストです。"; export const TextProvider = ({ children }) => { return <TextContext.Provider value={text}>{children}</TextContext.Provider>; }; App.js import { TextProvider } from "./TextProvider"; import { First } from "./First"; import "./styles.css"; export default function App() { return ( <div> <TextProvider> <First /> </TextProvider> </div> ); } First.js import { Second } from "./Second"; export const First = () => { return ( <> <p>Firstコンポーネント</p> <Second /> </> ); }; Second.js import { Third } from "./Third"; export const Second = () => { return ( <> <p>Secondコンポーネント</p> <Third /> </> ); }; Third.js import React, { useContext } from "react"; import { TextContext } from "./TextProvider"; export const Third = () => { const textData = useContext(TextContext); return ( <> <p> Thirdコンポーネント: <b>{textData}</b> </p> </> ); }; useStateを利用したcountデータを渡す [count, setCount]をProviderのvalueに渡すと、受け取ったコンポーネントから値を変更できる TextProvider.js import React, { createContext, useState } from "react"; export const TextContext = createContext(); // const text = "これはProviderから渡されたテキストです。"; export const TextProvider = ({ children }) => { const [count, setCount] = useState(0); return ( <TextContext.Provider value={[count, setCount]}> {children} </TextContext.Provider> ); }; App.jsは上のApp.jsと同じ First.jsは上のFirst.jsと同じ Second.jsは上のSecond.jsと同じ [count, setCount]を受け取り、値を参照・変更できる Third.js import React, { useContext } from "react"; import { TextContext } from "./TextProvider"; export const Third = () => { // const textData = useContext(TextContext); const [count, setCount] = useContext(TextContext); const handleClick = () => { setCount((prevCount) => prevCount + 1); }; return ( <> <p> Thirdコンポーネント: <b>現在のカウント: {count}</b> </p> <button onClick={handleClick}>+1ボタン</button> </> ); };
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【TypeScript】typeとinterfaceの違い

TypeScriptのtypeとinterfaceの違いをまとめています。 結論 typeエイリアスは、複数の場所で使い回す型に名前をつける。 interfaceは、オブジェクトや関数の構造を定義する。 具体的なtypeとinterfaceの違いは以下です。 ①同名の宣言 typeは、同名のtypeを宣言するとエラー。 interfaceは、同名のinterfaceを宣言するとマージされる。 ②継承 typeは、継承できない。 interfaceは、継承できる。 ①同名の宣言 typeは同名のtypeを宣言できません。(エラーになる。) type Person = { name: string age: number } type Person = { hight: number } //エラー interfaceは同名のinterfaceが宣言されるとマージされる。 interface Person { name: string age: number } interface Person { hight: number } //Personにhightというプロパティが付け足される。 const taro: Person = { name: "taro", age: 20, hight: 170 } 上記の場合、Person型を付けたtaroオブジェクトにはname、age、hightの3つのプロパティが必須になっています。 ②継承 typeは継承できません。 interfacemは、extendsを使って継承できます。 interface Person { name: string age: number } interface Employee extends Person { occupation: string } //Personを継承したEmployee型を定義 const taro: Employee = { name: "taro", age: 20, occupation: "sales", } ちなみにtypeは継承はできませんが、交差型で新しい型を作ることはできます。 type Person = { name: string age: number } type Hobby = { hobby1: string hobby2: string } type Introduction = Person & Hobby //交差型で新しい型を宣言 const taro: Introduction = { name: "taro", age: 20, hobby1: "tennis", hobby2: "shopping" } まとめ typeもinterfaceも実際ほとんど同じように使えますが、違いは以下。 ①同名の宣言 typeは、同名のtypeを宣言するとエラー。 interfaceは、同名のinterfaceを宣言するとマージされる。 ②継承 typeは、継承できない。 interfaceは、継承できる。 interfaceの方が柔軟に型を変化させることができるので、推奨されている様子です。 ただ、「柔軟に変化」できる分、思わぬバグを生みやすいとも言えそうなのでtypeが劣るというわけではない。 結論、違いを理解した上で、状況に応じて適切に使い分けていきましょう。 今回は以上です!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ライブラリなしでおしゃれなカラーピッカーを実装する【React】

はじめに <input type="color" />を使うことでカラーピッカー(ユーザーに色を選択させるUI)を簡単に実装できますが.見た目があまりかわいくない… そう思い,色々試してデザインする方法を編み出したので残しておきます. 結論 inputタグにopacity: 0;を設定することで透明にし,divタグで囲ってデザインする. 実装例 React+TypeScript+TailwindCSSで今回は円形にデザインした実装例です color.tsx const Color: React.VFC = () => { const [color, setColor] = useState<string>("#000000"); return ( <div className="w-8 h-8 rounded-full border border-gray-400" style={{ background: color }} > <input type="color" value={color} onChange={(e) => { setColor(e.target.value); }} className="w-full h-full opacity-0" /> </div> ); };
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む