- 投稿日:2020-10-14T21:08:17+09:00
【React+TypeScript】GETエラーを感知して、404ページに遷移させる
めちゃめちゃ久しぶりの投稿です。
先日までコミットしていたプロジェクトの中で自分のリサーチとスキルではどうにもできなかった404ページへのリダイレクトの処理について。
先輩に教えていただいたので備忘録として書いておきます。今回実現させたかったこと
今回実現させたかったのは、大きく分けて2つです。
- /hogehoge/tokenのようなURLが叩かれたときにtokenが正規のものか判断する
- 正規のものである場合、tokenが合致するデータをデータベースからGETする
- 正規のものでない場合、404ページへリダイレクトさせる
これらをReact+TypeScriptのプロジェクト内で実現させるというタスクでした。
行き詰まった点
「tokenが正しいかどうかとか、どうやって判断したらいいん。。。」
大きくいってこの点でした。
今までHTMLとCSSでやってきたので、もう何を使えばいいんかもわからずどう調べればいいのかも分かりませんでした。あとは今までリダイレクトさせる処理は.htaccessで記載してたので、それ以外でどうやった手法があるのかもわからず行き詰りました。
エラーを検知するtry...catch文
tokenの正誤判別にはtry...catch文を使いました。
MDNのドキュメントのリンクを貼っておきます。で、サンプルコードです。
try { // テストする文 } catch (e) { // エラー時に走る処理 }このような感じにtry...catch文は使われます。
なので、今回私が実現したかったことを実現しようとするとtry { const { data: hogehogeList } = await request.get({ uri: '/hoge', params: { token }, }); } catch (e) { // リダイレクト処理 }みたいな書き方になります。
こうすることで、tokenが正規のものである場合はデータベースから該当するリストをGETしてきて、
もしtokenが正規のものでない場合は、errorとなりリダイレクトの処理が走ります。ちなみにuriはAPIが置いてある場所で、paramsはデータをGETする際に必要なパラメータです。
リダイレクト処理をさせるモジュールhistory
リダイレクトさせるにあたって、今回はhistoryというモジュールを使用しました。
これを使用してリダイレクトの処理を実装しました。
参考にした記事は以下の記事になります。history.push();でアクションを起こした時にページ遷移をする
実際に書いたコードは以下になります。
try { const { data: hogehogeList } = await request.get({ uri: '/hoge', params: { token }, }); } catch (e) { history.push('/not-found'); window.location.reload(); }このようにすることで、先ほど書いたGETが失敗に終わった際には
404ページに遷移させることができました。まとめ
今回の総括ですが、
- try...catch文を使うことで、ある処理が失敗したときには別処理を走らせることができる
- historyを使うと、遷移させたいページに遷移させる処理を行うことができる
なお、このプロジェクトではreact-router-domを使用してルーティングさせてましたので、
上記の処理を実装する流れとなりました。先輩曰く、もっと良い処理があるらしいのですがそれはまた別の機会に。
- 投稿日:2020-10-14T19:18:38+09:00
react-autosuggest を React Hooks x TypeScript 上で導入したのでコード置いた
react-autosuggestのGitHubに書かれてるサンプルコードはクラスコンポーネントですが、Hooks勢であれば関数型で書き直したいと思うもの。
英語でも情報がなかったようなのでTypeScript込みで書き直してみた
GtiHubのサンプルコード
元ネタはこれ。一番基本的な使い方
GitHub: https://github.com/moroshko/react-autosuggest#installationsample.jsximport Autosuggest from 'react-autosuggest'; const languages = [ { name: 'C', year: 1972 }, { name: 'Elm', year: 2012 }, ... ]; const getSuggestions = value => { const inputValue = value.trim().toLowerCase(); const inputLength = inputValue.length; return inputLength === 0 ? [] : languages.filter(lang => lang.name.toLowerCase().slice(0, inputLength) === inputValue ); }; const getSuggestionValue = suggestion => suggestion.name; const renderSuggestion = suggestion => ( <div> {suggestion.name} </div> ); class Example extends React.Component { constructor() { super(); this.state = { value: '', suggestions: [] }; } onChange = (event, { newValue }) => { this.setState({ value: newValue }); }; onSuggestionsFetchRequested = ({ value }) => { this.setState({ suggestions: getSuggestions(value) }); }; suggestions. onSuggestionsClearRequested = () => { this.setState({ suggestions: [] }); }; render() { const { value, suggestions } = this.state; const inputProps = { placeholder: 'Type a programming language', value, onChange: this.onChange }; return ( <Autosuggest suggestions={suggestions} onSuggestionsFetchRequested={this.onSuggestionsFetchRequested} onSuggestionsClearRequested={this.onSuggestionsClearRequested} getSuggestionValue={getSuggestionValue} renderSuggestion={renderSuggestion} inputProps={inputProps} /> ); } }関数がいろいろあってややこしいライブラリだが基本的bに1つの関数名(及びstate)と1つのpropsが対応していると見るとわかりやすい(getSuggests関数とonChange関数は別)
sample.jsx<Autosuggest suggestions={suggestions} // suggestionsはサジェスト候補のオブジェクトが格納されたstate onSuggestionsFetchRequested={this.onSuggestionsFetchRequested} // suggestionsの状態を更新する関数 onSuggestionsClearRequested={this.onSuggestionsClearRequested} // suggestionsを初期状態にする関数 getSuggestionValue={getSuggestionValue} // langagesのオブジェクトからnameを取り出す関数 renderSuggestion={renderSuggestion} // サジェスト一覧表示に使うJSXを返す inputProps={inputProps} // placeholder, value, onChange の初期値を渡す />Hooks & TypeScript で置き換えた
型ファイルも一緒に落としておく
yarn add react-autosuggest @types/react-autosuggest使うデータへLangTypeの型を宣言したり、useStateを使ったりクラスを関数に書き変えたりして置き換えた。
codesandbox: https://codesandbox.io/s/react-autosuggest-typescript-ptn91?fontsize=14&hidenavigation=1&theme=dark
ReactSuggest.tsximport React, { BaseSyntheticEvent, FC, useState } from "react"; import Autosuggest from "react-autosuggest"; type LangType = { name: string; year: number; }; const languages: LangType[] = [ { name: "CCC", year: 1972 }, { name: "CyberMan", year: 1972 }, { name: "EtherNet", year: 2012 }, { name: "Entertainer", year: 10000 } ]; const getSuggestions = (value: string): LangType[] => { const inputValue = value.trim().toLowerCase(); const inputLength = inputValue.length; return inputLength === 0 ? [] : languages.filter( (lang) => lang.name.toLowerCase().slice(0, inputLength) === inputValue ); }; const ReactSuggestion: FC = () => { const [value, setValue] = useState(""); const [suggestions, setSuggestions] = useState<LangType[]>([]); const getSuggestionValue = (suggestion: LangType): string => { const { name } = suggestion; return name; }; const renderSuggestion = (suggestion: LangType) => { return <div>{suggestion.name}</div>; }; const onChange = ( event: BaseSyntheticEvent, { newValue }: { newValue: string } ) => { if (event) setValue(newValue); }; const onSuggestionsFetchRequested = ({ value }: { value: string }) => { const suggestions: LangType[] = getSuggestions(value); setSuggestions(suggestions); }; // Autosuggest will call this function every time you need to clear suggestions. const onSuggestionsClearRequested = () => { setSuggestions([]); }; const inputProps = { placeholder: "cかeを入力してみて", value, onChange }; return ( <Autosuggest suggestions={suggestions} onSuggestionsFetchRequested={onSuggestionsFetchRequested} onSuggestionsClearRequested={onSuggestionsClearRequested} getSuggestionValue={getSuggestionValue} renderSuggestion={renderSuggestion} inputProps={inputProps} /> ); }; export default ReactSuggestion;まとめ
こんな感じで動く
onChange関数の引数に使わないけれど event を入れておかないと怒られた。他にもオプションがあるのでGitHubリポジトリの方で確認してもらえればと思う。
GitHub: https://github.com/moroshko/react-autosuggest#installation
- 投稿日:2020-10-14T19:18:38+09:00
react-autosuggest を React Hooks x TypeScript 上に入れてサジェスト機能を実装した
react-autosuggestのGitHubに書かれてるサンプルコードはクラスコンポーネントですが、Hooks勢であれば関数型に書き直したいと思うもの。
英語でも情報がなかったようなのでTypeScript込みで書き直してみた
この記事の完成品
こんな感じで動くやつになる
【元ネタ】GtiHubのサンプルコード
元ネタはこれ。一番基本的な使い方
GitHub: https://github.com/moroshko/react-autosuggest#installationsample.jsximport Autosuggest from 'react-autosuggest'; const languages = [ { name: 'C', year: 1972 }, { name: 'Elm', year: 2012 }, ... ]; const getSuggestions = value => { const inputValue = value.trim().toLowerCase(); const inputLength = inputValue.length; return inputLength === 0 ? [] : languages.filter(lang => lang.name.toLowerCase().slice(0, inputLength) === inputValue ); }; const getSuggestionValue = suggestion => suggestion.name; const renderSuggestion = suggestion => ( <div> {suggestion.name} </div> ); class Example extends React.Component { constructor() { super(); this.state = { value: '', suggestions: [] }; } onChange = (event, { newValue }) => { this.setState({ value: newValue }); }; onSuggestionsFetchRequested = ({ value }) => { this.setState({ suggestions: getSuggestions(value) }); }; suggestions. onSuggestionsClearRequested = () => { this.setState({ suggestions: [] }); }; render() { const { value, suggestions } = this.state; const inputProps = { placeholder: 'Type a programming language', value, onChange: this.onChange }; return ( <Autosuggest suggestions={suggestions} onSuggestionsFetchRequested={this.onSuggestionsFetchRequested} onSuggestionsClearRequested={this.onSuggestionsClearRequested} getSuggestionValue={getSuggestionValue} renderSuggestion={renderSuggestion} inputProps={inputProps} /> ); } }関数がいろいろあってややこしいライブラリだが基本的に1つの関数名(及びstate)と1つのpropsが対応していると見るとわかりやすい(getSuggests関数とonChange関数は別)
sample.jsx<Autosuggest suggestions={suggestions} // suggestionsはサジェスト候補のオブジェクトが格納されたstate onSuggestionsFetchRequested={this.onSuggestionsFetchRequested} // suggestionsの状態を更新する関数 onSuggestionsClearRequested={this.onSuggestionsClearRequested} // suggestionsを初期状態にする関数 getSuggestionValue={getSuggestionValue} // langagesのオブジェクトからnameを取り出す関数 renderSuggestion={renderSuggestion} // サジェスト一覧表示に使うJSXを返す inputProps={inputProps} // placeholder, value, onChange の初期値を渡す />Hooks & TypeScript で置き換えた
型ファイルも一緒に落としておく
yarn add react-autosuggest @types/react-autosuggest使うデータへLangTypeの型を宣言したり、useStateを使ったりクラスを関数に書き変えたりして置き換えた。
codesandbox: https://codesandbox.io/s/react-autosuggest-typescript-ptn91?fontsize=14&hidenavigation=1&theme=dark
ReactSuggest.tsximport React, { BaseSyntheticEvent, FC, useState } from "react"; import Autosuggest from "react-autosuggest"; type LangType = { name: string; year: number; }; const languages: LangType[] = [ { name: "CCC", year: 1972 }, { name: "CyberMan", year: 1972 }, { name: "EtherNet", year: 2012 }, { name: "Entertainer", year: 10000 } ]; const getSuggestions = (value: string): LangType[] => { const inputValue = value.trim().toLowerCase(); const inputLength = inputValue.length; return inputLength === 0 ? [] : languages.filter( (lang) => lang.name.toLowerCase().slice(0, inputLength) === inputValue ); }; const ReactSuggestion: FC = () => { const [value, setValue] = useState(""); const [suggestions, setSuggestions] = useState<LangType[]>([]); const getSuggestionValue = (suggestion: LangType): string => { const { name } = suggestion; return name; }; const renderSuggestion = (suggestion: LangType) => { return <div>{suggestion.name}</div>; }; const onChange = ( event: BaseSyntheticEvent, { newValue }: { newValue: string } ) => { if (event) setValue(newValue); }; const onSuggestionsFetchRequested = ({ value }: { value: string }) => { const suggestions: LangType[] = getSuggestions(value); setSuggestions(suggestions); }; // Autosuggest will call this function every time you need to clear suggestions. const onSuggestionsClearRequested = () => { setSuggestions([]); }; const inputProps = { placeholder: "cかeを入力してみて", value, onChange }; return ( <Autosuggest suggestions={suggestions} onSuggestionsFetchRequested={onSuggestionsFetchRequested} onSuggestionsClearRequested={onSuggestionsClearRequested} getSuggestionValue={getSuggestionValue} renderSuggestion={renderSuggestion} inputProps={inputProps} /> ); }; export default ReactSuggestion;まとめ
onChange関数の引数に使わないけれど event を入れておかないと怒られた。他にもオプションがあるのでGitHubリポジトリの方で確認してもらえればと思う。
GitHub: https://github.com/moroshko/react-autosuggest#installation
- 投稿日:2020-10-14T18:04:07+09:00
【小ネタ】その辺のReact(Vue)のGitHubリポジトリをcodesandboxで開く
超小ネタ注意報
「初めて出会ったあの時から知ってるわい」な人が多い気がするのだけど今まで全く気がつかなかったので同士へ向けて記事を書いてみた。
Reactでオートコンプリート(サジェスト機能)を実装したかったけどyarnで引っ張るの面倒だなあ「あれ、実はcodesandboxで普通にImportできるのでは?」と思ったら普通にできたので自分のケースでこの小ネタを紹介。
ソース
海外エンジニアYoutuberのBigless27さんの動画に貼ってあったポケモンの名前のオートコンプリートの実装サンプル動画が主の元ネタだ
Custom Autocomplete in React with Hooks! - https://www.youtube.com/watch?v=vXO5JMiKtM8&ab_channel=LessononCoding今回引っ張ってくるGitHubリポジトリのリンクはこちら https://github.com/Bigless27/react-autocompelte
codesandboxの左側メニューの「Repositories」から「Import Repository」を選ぶ
先ほどのGitHubリポジトリの動画を貼って「Import and Fork」する
するとこんな風にimportできた
感想
1千兆年より前から知っておきたかったしこういう小さなところで便利な世界はきっともっとあるんだろうな
- 投稿日:2020-10-14T11:24:06+09:00
Storybook v6.0新機能まとめ
はじめに
https://storybook.js.org/releases/6.0
2020年8月10日にStorybook6.0がリリースされました。
5.3からアップグレードを行う際に調べた新機能や変更点などを自分なりにおおまかにまとめてみました。
基本的にはドキュメントや公式の記事に書いてある内容になるので詳しく知りたい方はこちらへ※間違いなどがありましたらご指摘お願いします。
Storybook 5.3からの主な変更点
ざっくり
- セットアップが爆速でできるようになった
- ストーリーの再利用性があがった
- 複数のStorybookを一つにまとめられるようになった
- ドキュメントが充実
Zero-config
公式がconfig書かなくていいよ!と言っている通り、セットアップが非常に簡単になりました。
具体的には以下になります。
- Typescriptのサポート
- Storybook Essentialsの登場
Typescriptのサポート
Typescriptサポートが組み込まれました。これにより、プロジェクト内のTS固有のwebpackやbabel構成を削除できるようになりました。指定がない限りは各フレームワークの基本構成を使用するそうです。わかりやすい。
Storybook Essentials
開発チームが厳選した、ほぼ必須アドオン達のよくばりパックです。もちろん面倒な設定を書く必要はありません。
Storybookには公式アドオンの他に多くのサードパーティアドオンがあり、カスタマイズ性がありましたが、ありすぎるが故にセットアップが困難になる可能性がありました。
このEssentialsはinitをしただけで勝手に追加され、よしなに動いてくれます。どれだけわかりやすいんだ。Storybook Args
ArgsはStorybookの記法であるComponent StoryFormat(CSF)の新しい概念です。
コンポーネントのpropsの初期値の宣言や再利用ができるようになります。こうかいていたのが
export const Default = () => ( <Button label="hello" backgroundColor="#222222" /> ); export const Disabled = () => ( <Button label="hello" backgroundColor="#222222" disabled={true} /> ); export const Large = () => ( <Button label="hello" backgroundColor="#222222" size="large" /> )Storybook Argsの場合こうやってかけます
const Template = (args) => <Button {...args} />; export const Default = Template.bind({}); Default.args = { label: "hello", backgroundColor: "#222222" }; export const Disabled = Template.bind({}); Disabled.args = { ...Default.args, disabled:true }; export const Large = Template.bind({}); Loading.args = { ...Default.args, size: "large" };propsの数が多くなればなるほど、恩恵を受けることができます。
また、この書き方はもはやStorybookに依存していないためJestやその他ツールなどでも利用できます。
便利です。Controls
Storybook上からコンポーネントを動的に編集することができる新しいアドオンです。Essentialsに含まれており、特別なセットアップは必要ありません。
Storybook Argsで書いた場合コントロールが自動生成されます。べんり。
Composition
複数のStorybookを1つにまとめる事ができます。
異なるフレームワークを使用したプロジェクト間でも、Storybookを切り替えることなく共通デザインシステムを参照したり、全体感を確認できるようになりました。
筆者はまだ試せていないので、詳細はこちらへ公式ドキュメント
v6.0に合わせて一新されました。
チュートリアルやガイドが非常に充実していて、わかりやすかったです。まとめ
v6.0からの新機能や変更点をおおまかにまとめてみました。
これ以外にも非推奨になったAPIやアドオンがあったり、変更された仕様などがあるので確認したい方は公式マイグレーションガイドを参照してください。参考
- 投稿日:2020-10-14T08:37:51+09:00
作成済のRailsアプリケーションのView部分をReactに置き換えてみる
初めてのRailsアプリケーションを作成してから数か月、Reactの学習を兼ねて View を編集していこうと思います。
Railsアプリの確認
既に忘れている部分も多いのでまずはディレクトリ内の確認
直下に
package.json他は rails new で作成されたものだと思う。
yarn が必要だという事なので確認
nvm -v
not found
npm -v
6.14.7
yarn -v
1.22.4bin/ にインストールされているみたい。いつインストールしたのかは記憶にない。
次は gem webpacker が必要という事
Gemfilegem 'webpacker'追記したら
$ bundle install必要なファイルの作成&インストール
$ bin/rails webpacker:install作成された app/javascript/ app/javascript/packs/ ディレクトリ内にファイルを作成していくみたいです。
React のインストール
$ bin/yarn add react react-domJSX を使用する
デフォルトでは JSX は使用できないようです。
$ bin/rails webpacker:install:reactWebpacker now supports react.js って出た。
あーさっきのコマンドで初めてwebpackerがreactを認識するんですね。
JSX は使えなくて当然でした・・・。app/javascript/packs/ に hello_react.jsx が作成された。
app/views/layouts/application.html.slim 内に
app/views/layouts/application.html.slim<head> = javascript_pack_tag 'hello_react' </head>で サンプルが表示される。
最後に MaterialUi をインストール
$ bin/yarn add @material-ui/core $ bin/yarn add @material-ui/icons次回から少しずつ変更していこうと思います。
- 投稿日:2020-10-14T07:24:23+09:00
Reactのディレクトリ構成どうしてる?
前置き
Reactのスターターキットを作る上で、ディレクトリ構成について迷いながらも考えてみたので、その構成について紹介したいと思います。
構成について言及している記事はまだ少なく、ベスト構成とというものは存在しないと思います。
今回紹介する構成もまだまだ改良していかなければいけないと思いますが、構成について迷っている方に少しでも役立てればと思います。開発環境について
node v12.13.1
npm v6.13.4使用している技術について
- React (reactHooks)
- Redux (Redux-toolkit)
- TypeScript
今回作成したスターターは、redux-toolkitを使っています。
小さいプロジェクトにはredux不要では??という考えもあると思います。(自分もちょっとそう思います)
ですが、redux-toolkitの登場によってreduxを簡単に使えるようになったこと、
またreduxを追加実装するコストを考えると最初から入れてしまった方がいいのでは、という考えのもと、初期から含めることにしました!
↓この記事にものすごく感銘を受けました!
TypeScriptでReactをやるときは、小さいアプリでもReduxを最初から使ってもいいかもねというお話
公式での扱いは??
Reactは推奨されるディレクトリ構成はありません。
公式でも考えすぎるべきではないと書かれています。React はファイルをどのようにフォルダ分けするかについての意見を持っていません。
まだプロジェクトを始めたばかりなら、ファイル構成を決めるのに 5 分以上かけないようにしましょう。上述の方法の 1 つを選ぶか、自分自身の方法を考えて、コードを書き始めましょう! おそらく実際のコードをいくらか書けば、なんにせよ考え直したくなる可能性が高いでしょう。
規模によって適切なディレクトリ構成が変わり、一つの答えはありません。
また、React周りは新しいツールがどんどん出てきて、何を使うか、どのように使用するかは目まぐるしく変わっているように感じます。
それも相まってディレクトリ構成を決めることが難しくなってるのだと思います。構成について
さて本題です!
早速ですが実際作成したコードはこちら↓
https://github.com/Sotq17/rtk_starter簡単にどのディレクトリでどういったファイルを扱うかを説明したいと思います。
(実際のコードを見てもらえると早いかもしれません…)
構成は以下の通りになっています。─── src ├── img (画像置き場) ├── pug (render先を指定) ├── ts (react外でjsを使うときに使用) ├── tsconfig.json └── tsx ├── index.tsx (エントリーポイント) ├── stores │ ├── index.ts (slicesディレクトリで作られたSliceを結合する) │ └── slices (このディレクトリ下で各sliceファイルを扱う) ├── style(各page,componentのcssを切り分けたい時に使用) │ ├── GlobalStyle.tsx │ ├── components │ │ ├── atoms │ │ └── block │ ├── pages │ ├── resetStyle.tsx │ └── variables.tsx ├── utils(定数などを管理) └── views ├── components(使い回しのできる要素) │ ├── atoms(最小単位のcomponent) │ ├── block(atomsを組み合わせたり、atomsでは管理しきれないcomponent) │ └── modules(機能を持ったcomponent) └── pages(各ページの呼び出し先) ├── login └── top
index.pug
にtsx/index.tsx
をrenderする形なので、tsxディレクトリ以下を紹介していきます!index.tsx
エントリーポイントです。
各ページを呼び出し、react-router-domでルーティングを行う役割です。index.tsxconst app = document.getElementById('app') // page import Top from './views/pages/top/Top' import Login from './views/pages/login/Login' // react-router-domでページ遷移 ReactDOM.render( <Provider store={store}> <GlobalStyle /> <Router> <Switch> <Route path="/top" component={Top} /> <Route path="/" component={Login} /> </Switch> </Router> </Provider>, app )views
└── views ├── components(使い回しのできる要素) │ ├── atoms(最小単位のcomponent) │ │ └── Button.tsx │ ├── block(atomsを組み合わせたり、atomsでは管理しきれないcomponent) │ │ └── Header.tsx │ └── modules(機能を持ったcomponent) │ └── ScrollTop.tsx └── pages(各ページの呼び出し先) └── 各ページのディレクトリ └── Login.tsxpages
└── pages(各ページの呼び出し先) ├── Top │ ├── TopList.tsx │ └── Top.tsx └── Login └── Login.tsx上記の
tsx/index.tsx
に呼び出されるコンポーネントです。
各ページに1つこの要素が存在し、そのページ内で使われるコンポーネントはさらにここから呼び出して使用します。
また、そのページにしかない固有の要素は同ディレクトリ内に配置します。
上記のtreeで言うと、TopList.ts
が固有の要素にあたります。例↓
top.tsxconst Top = () => { return ( <div> <Header /> <h1 css={TopTitle}>TOP</h1> <TopList /> <Footer /> </div> ) }components
pageとは異なる、使い回すことのできる要素をこちらに置きます。
その中にも意味合いが異なる要素が多いため、さらに下記のディレクトリに分けていきます。
atomicデザインでできれば良かったのですが、知見がないためとりあえず下記のようにざっくり作ってます)├── components(使い回しのできる要素) ├── atoms │ ├── block │ └── modules
- atoms
- block
- module
atoms
ボタンなどの最小の要素です。変更出来るよう、styleや機能は流し込めるように作成します。
Button.tsximport React from 'react' import { css } from '@emotion/core' export const Button = props => { const button = css({ backgroundColor: `${props.bgColor}`, color: `${props.color}`, border: `1px solid ${props.bgColor}`, padding: '8px 16px', borderRadius: '4px', cursor: 'pointer' }) return ( <button css={button} onClick={props.onClick}> {props.name} </button> ) }ちなみに呼び出すのはこんな感じです↓
Sample.tsxconst Sample = () => { const handleClick = () => { console.log("click!") } return ( <div> <Button onClick={handleClick} name="Click!" color="#ffffff" bgColor="#33333" /> </div> ) }block
ページに1個しかないHeaderやFooter、また使い回すけどatomsを組み合わせて使う要素を置きます。
modules
atomsへの機能の流し込みが難しかったり、そのコンポーネント固有の機能があるものをここに置きます。
例えば、パンくずリストなどが該当します。パンくずではないですが、サンプルです↓(この程度ならatomsでいいですけど…)
ScrollTopBtn.tsxexport const ScrollTop = () => { const scrollToTop = () => { scroll.scrollToTop() } return ( <div css={ButtonContainer} onClick={scrollToTop}> <p css={ButtonContent}>↑</p> </div> ) }store
reduxで使用する要素は全てこちらに配置します。
redux-toolkitを使用しておりますが、使い方に関しては今回割愛します。slicesディレクトリ以下で Reducer / Actionを定義し(createSlice)、
store/index.tsx
でslicesディレクトリ以下で作られたファイルを結合するといった流れです。├── stores ├── index.ts └── slices └── userSlice.tsindex.tsimport { configureStore } from '@reduxjs/toolkit' import loginReducer from './slices/userSlice' // それぞれのSliceを呼び出して結合する export default configureStore({ reducer: { // 識別する名前: importしてきたReducer名 login: loginReducer } })userSlice.tsimport { createSlice, createAsyncThunk } from '@reduxjs/toolkit' import axios from 'axios' import {apiURL} from "../../utils/constants" // 非同期はSliceの外に出してcreateAsyncThunkを使用する export const fetchAsyncLogin = createAsyncThunk('login/get', async () => { // ログインAPIを叩く想定 await axios.get(apiURL) // console.log(res.data) }) const loginSlice = createSlice({ // slice名 name: 'login', // 初期値 initialState: { auth: { username: '', password: '' }, isLogin: false }, //各reducer 第一引数でstate情報を受け取り、第二引数でユーザーが操作した情報を受け取る reducers: { editUsername: (state, action) => { state.auth.username = action.payload }, editPassword: (state, action) => { state.auth.password = action.payload }, logout: (state, action) => { state.isLogin = false } }, // 非同期の結果を受け取る extraReducers: builder => { builder.addCase(fetchAsyncLogin.fulfilled, (state, action) => { state.isLogin = true }) } }) // actionをexport export const { editUsername, editPassword, logout } = loginSlice.actions // state情報をexport export const selectUser = (state: any) => state.login // reducerをexport → storeへ export default loginSlice.reducerstyle
resetCSSや、CSS変数などを定義するために使います。
また、emotionを使っているのでtsxファイル内に書くことができますが、あえて分けたいと言う場合に使用します。
例えば、tsxファイル内が肥大化してしまう場合や、同じcssを使い回す場合などです(コンポーネント切り分けるのが難しいこともあると思うので…)。ディレクトリの分け方はとりあえずviewsディレクトリと同じ構成にしております。
├── style(各page,componentのcssを切り分けたい時に使用) ├── GlobalStyle.tsx ├── components │ ├── atoms │ │ └── Button.tsx │ └── block │ ├── Footer.tsx │ └── Header.tsx ├── pages │ ├── Login.tsx │ └── Top.tsx ├── resetStyle.tsx └── variables.tsxutils
tsx内で使用する変数、定数など、全体で使用する要素を置きます。
├── utils(定数などを管理) └── constants.tsxconstants.tsxexport const apiURL = '' // など ...全体
src下の全体は以下のような形です!
─── src ├── img (画像置き場) │ └── common │ └── favicon.png ├── pug (render先を指定) │ └── index.pug ├── ts (react外でjsを使うときに使用) │ └── app.ts ├── tsconfig.json └── tsx ├── index.tsx (エントリーポイント) ├── stores │ ├── index.ts (slicesディレクトリで作られたSliceを結合する) │ └── slices (このディレクトリ下で各sliceファイルを扱う) │ └── userSlice.ts ├── style(各page,componentのcssを切り分けたい時に使用) │ ├── GlobalStyle.tsx │ ├── components │ │ ├── atoms │ │ │ └── Button.tsx │ │ └── block │ │ ├── Footer.tsx │ │ └── Header.tsx │ ├── pages │ │ ├── Login.tsx │ │ └── Top.tsx │ ├── resetStyle.tsx │ └── variables.tsx ├── utils(定数などを管理) │ └── constants.tsx └── views ├── components(使い回しのできる要素) │ ├── atoms(最小単位のcomponent) │ │ └── Button.tsx │ ├── block(atomsを組み合わせたり、atomsでは管理しきれないcomponent) │ │ ├── Footer.tsx │ │ └── Header.tsx │ └── modules(機能を持ったcomponent) │ └── ScrollTop.tsx └── pages(各ページの呼び出し先) ├── login │ └── Login.tsx └── top ├── Top.tsx └── TopList.tsx(そのページでしか使われないreact-componentは同階層に設置)作ってみた感想
これを作る上でご意見、ご協力いただいた方々には最高に感謝です
まだまだ雑なところが多く、使ってみて思うことはたくさんあるのですが、
views
とstore
で分ける構成は悪くないんじゃないかな…と思ってます。
うちではこうしてる、ここはこうした方が良いなど、ご意見いただけますと幸いです