20210715のReactに関する記事は15件です。

【React】faviconの設定をする

画像を作る、探す 設定したいfaviconの画像を作ったり探したりします。 今回はすでに作成されているものをこちらのサイトからお借りします。 なお、ブラウザによってfaviconで使う画像サイズが違うため、対応させたいブラウザのサイズにしましょう。 すでに設定したい画像がある場合も、画像サイズの調整をしましょう。 画像引用: https://sdesignlabo.com/web/favicon/ ちなみに、オリジナルのものを作成したいときは、こちらのサイトでも作成できるようです。 実装方法 今回はReactで実装していきます。 実装方法は2ステップ。 まず、設定したい画像を配置します。 配置するディレクトリは、public直下になります、 2つ目にpublic直下のindex.htmlファイルのfaviconを設定している箇所を修正します。 初期値は「favicon.ico」で、最初に配置した画像の名前が同様ならこのステップは必要ありません。 index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />#ここを設定したい画像の名前にする <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="theme-color" content="#000000" /> <meta name="description" content="Web site created using create-react-app" /> <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> <!-- manifest.json provides metadata used when your web app is installed on a user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/ --> <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> <!-- Notice the use of %PUBLIC_URL% in the tags above. It will be replaced with the URL of the `public` folder during the build. Only files inside the `public` folder can be referenced from the HTML. Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> <title>React App</title> </head> <body> <noscript>You need to enable JavaScript to run this app.</noscript> <div id="root"></div> <!-- This HTML file is a template. If you open it directly in the browser, you will see an empty page. You can add webfonts, meta tags, or analytics to this file. The build step will place the bundled scripts into the <body> tag. To begin the development, run `npm start` or `yarn start`. To create a production bundle, use `npm run build` or `yarn build`. --> </body> </html> これでfaviconの設定が完了です。アプリを起動して実際に確認してみましょう。 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

react-boilerplate基礎編

react-boilerplateとは オフィシャルサイトによると A highly scalable, offline-first foundation with the best DX and a focus on performance and best practices パフォーマンスとDX(デベロッパーエクスペリエンス)重視のスケーラブル、オフラインファーストな基盤と言ったところでしょうか。 要はプロダクションレベルのreactアプリケーションを工数かけずに立ち上げできるツールですね。 こちらはReact業界ではかの有名なMax Stoiberさんを筆頭に、多数の開発者による、開発者のための、オープンソース開発ツールです。 Max StoiberさんによるスケーラブルなReact開発についての動画が公開されているので、興味ある方はぜひご覧ください。 特徴 scaffoldで素早くテンプレを生成できる redux/redux-sagaによりステートの管理が楽になる オフラインファーストでネット環境が悪くても使用できる リンティングとユニットテストがデフォルトでついてくるので初期のセットアップ が楽 といったところでしょうか。ただし個人的にはルールでガチガチになって後々拡張性に困ってしまうのが心配な方には向いていないと思います。 始める前に こちらはReactをある程度経験している方向けのツールなので、まずは下記の知識について学んでから使いましょう。React初心者はこちらを参考すると良いでしょう。 コア React React Router Redux Redux Saga Reselect Immer Styled Components ユニットテスト Jest react-testing-library リント ESLint Prettier stylelint 初めてみる 依存 まずは以下がセットアップされていることを確認しましょう。 Node.js v8.15.1以上 npm v5以上 されていない方はこちらを参照してください。 初期設定 root git clone --depth=1 https://github.com/react-boilerplate/react-boilerplate.git <プロジェクト名> cd <プロジェクト名> npm run setup npm start これでhttp://localhost:3000からサンプルアプリを見れます。 ジェネレーター 常用する機能を自動で生成する機能です。こちらを使えば大規模な開発でもフォーマットがある程度保たれます。 生成できる機能はコンテナ、コンポーネントと言語です。 Max Stoiberさんはコンテナ&コンポーネントのアーキテクチャを推していて、コードをタイプ(アクション、スタイルなど)で分けるのではなく、フィーチャーごとに分けるべきという考えです。 大きいカテゴリに分けると コンポーネント: データとの連携はしない見た目重視のパーツ DOMマークアップとスタイルを持つ 再利用ができる 他の部分への依存がない Propsでのみデータとインタラクトする 例:ページ、ナビゲーションバー、リスト、ボタン コンテナ: 機能(どう動くか)重視のパーツ DOMマークアップとスタイルを持たない(divなどは除く) コンポーネントのデータの動作の設定 react-boilerplateはi18nが最初からサポートしているので新しい言語の生成、及びメッセージの設定がとても簡単にできます。 新しいコンテナを生成してみる 例えばイベントを宣伝するページを作成するとしましょう。イベントページではおすすめのイベントと全てのイベントのデータを別々にゲットして、ページにレンダーする流れです。 root npm run generate container ? What should it be called? EventPage ? Do you want to wrap your component in React.memo? Yes ? Do you want headers? Yes ? Do you want an actions/constants/selectors/reducer tuple for this container? Yes ? Do you want sagas for asynchronous flows? (e.g. fetching data) Yes ? Do you want i18n messages (i.e. will this component use text)? Yes ? Do you want to load resources asynchronously? Yes これでapp/containers/EventPage/が作成されて、必要なファイルを自動的に繋げてくれます。 Reduxを設定 コンスタンツの定義 まずはReduxのアクションに必要なコンスタンツを設定します。 app/containers/EventPage/constants.js // おすすめイベントのアクション export const LOAD_FEATURED_EVENTS = 'app/EventPage/LOAD_FEATURED_EVENTS'; export const LOAD_FEATURED_EVENTS_SUCCESS = 'app/EventPage/LOAD_FEATURED_EVENTS_SUCCESS'; export const LOAD_FEATURED_EVENTS_FAILURE = 'app/EventPage/LOAD_FEATURED_EVENTS_FAILURE'; // 全てのイベントのアクション export const LOAD_EVENTS = 'app/EventPage/LOAD_FEATURED_EVENTS'; export const LOAD_EVENTS_SUCCESS = 'app/EventPage/LOAD_FEATURED_EVENTS_SUCCESS'; export const LOAD_EVENTS_FAILURE = 'app/EventPage/LOAD_FEATURED_EVENTS_FAILURE'; アクションの定義 app/containers/EventPage/actions.js import { LOAD_EVENTS, LOAD_EVENTS_SUCCESS, LOAD_EVENTS_FAILURE, LOAD_FEATURED_EVENTS, LOAD_FEATURED_EVENTS_SUCCESS, LOAD_FEATURED_EVENTS_FAILURE, } from './constants'; // おすすめイベントのアクション export function loadFeaturedEvents(userId, skip, take) { return { type: LOAD_FEATURED_EVENTS, payload: { userId, skip, take, }, }; } export function loadFeaturedEventsSuccess(featuredEvents) { return { type: LOAD_FEATURED_EVENTS_SUCCESS, payload: { featuredEvents, }, }; } export function loadFeaturedEventsFailure(errors) { return { type: LOAD_FEATURED_EVENTS_FAILURE, payload: { errors, }, }; } // 全てのイベントのアクション export function loadEvents(userId, skip, take, keyword) { return { type: LOAD_EVENTS, payload: { userId, skip, take, keyword, }, }; } export function loadEventsSuccess(events) { return { type: LOAD_EVENTS_SUCCESS, payload: { events, }, }; } export function loadEventsFailure(errors) { return { type: LOAD_EVENTS_FAILURE, payload: { errors, }, }; } Reducerの定義 app/containers/EventPage/reducers.js import produce from 'immer'; import { LOAD_EVENTS, LOAD_EVENTS_SUCCESS, LOAD_EVENTS_FAILURE, LOAD_FEATURED_EVENTS, LOAD_FEATURED_EVENTS_SUCCESS, LOAD_FEATURED_EVENTS_FAILURE, } from './constants'; export const initialState = { // イベントのステート events: [], eventsLoading: false, eventsErrors: false, // おすすめイベントのステート featuredEvents: [], featuredEventsLoading: false, featuredEventsErrors: false, }; /* eslint-disable default-case, no-param-reassign */ const eventPageReducer = (state = initialState, action) => produce(state, draft => { switch (action.type) { case LOAD_EVENTS: draft.eventsLoading = true; draft.eventsErrors = false; break; case LOAD_EVENTS_SUCCESS: draft.eventsLoading = false; draft.eventsErrors = false; draft.events = action.payload.events; break; case LOAD_EVENTS_FAILURE: draft.eventsLoading = false; draft.eventsErrors = action.payload.errors; break; case LOAD_FEATURED_EVENTS: draft.featuredEventsLoading = true; draft.featuredEventsErrors = false; break; case LOAD_FEATURED_EVENTS_SUCCESS: draft.featuredEventsLoading = false; draft.featuredEventsErrors = false; draft.featuredEvents = action.payload.featuredEvents; break; case LOAD_FEATURED_EVENTS_FAILURE: draft.featuredEventsLoading = false; draft.featuredEventsErrors = action.payload.errors; break; } }); export default eventPageReducer; Selectorの定義 app/containers/EventPage/selectors.js import { createSelector } from 'reselect'; import { initialState } from './reducer'; /** * Direct selector to the eventPage state domain */ const selectEventPageDomain = state => state.eventPage || initialState; /** * Featured events selector used by EventPage */ const makeFeaturedEventsSelector = () => createSelector( selectEventPageDomain, substate => substate.get('featuredEvents'), ); /** * Events selector used by EventPage */ const makeEventsSelector = () => createSelector( selectEventPageDomain, substate => substate.get('events'), ); export { selectEventPageDomain, makeFeaturedEventsSelector, makeEventsSelector, }; sagaを設定 データをasyncで取得する一番肝の部分になります。今回api連携は主ではないので、モックデータを作ってそこから引っ張りたいと思います。 root mkdir app/containers/EventPage/mocks touch app/containers/EventPage/mocks/events.js touch app/containers/EventPage/mocks/featuredEvents.js app/containers/EventPage/mocks/events.js // イベントのデータ const events = [ { id: '1', title: 'event 1', }, { id: '2', title: 'event 2', }, { id: '3', title: 'event 3', }, ]; export default events; app/containers/EventPage/mocks/featuredEvents.js // おすすめイベントのデータ const featuredEvents = [ { id: '1', title: 'featured event 1', }, { id: '2', title: 'featured event 2', }, ]; export default featuredEvents; それらをsagaにインポートしますが、本番ではfetchの関数内でデータを引っ張ります。 app/containers/EventPage/saga.js import { all, put, takeLatest } from 'redux-saga/effects'; import { LOAD_EVENTS, LOAD_FEATURED_EVENTS } from './constants'; import { loadEventsFailure, loadEventsSuccess, loadFeaturedEventsFailure, loadFeaturedEventsSuccess, } from './actions'; // モックデータを取得 import events from './mocks/events'; import featuredEvents from './mocks/featuredEvents'; function* fetchEvents(action) { // ここでイベントのapiからデータを取得 try { yield put(loadEventsSuccess(events)); } catch (e) { yield put(loadEventsFailure(e)); } } function* loadEvents() { // takeLatestで一番最新のアクションのみに反応する yield takeLatest(LOAD_EVENTS, fetchEvents); } function* fetchFeaturedEvents(action) { // ここでおすすめイベントのapiからデータを取得 try { yield put(loadFeaturedEventsSuccess(featuredEvents)); } catch (e) { yield put(loadFeaturedEventsFailure(e)); } } function* loadFeaturedEvents() { yield takeLatest(LOAD_FEATURED_EVENTS, fetchFeaturedEvents); } export default function* eventPageSaga() { // 多数のアクションをまとめる yield all([loadEvents(), loadFeaturedEvents()]); } index.jsを編集 app/containers/EventPage/index.js ... // セレクターとアクションをインポート - import makeSelectEventPage from './selectors'; + import { makeEventsSelector, makeFeaturedEventsSelector } from './selectors'; + import { loadEvents, loadFeaturedEvents } from './actions'; ... // データとディスパッチをプロップとして関数に入れる export function EventPage({ + events, + featuredEvents, + onLoadEvents, + onLoadFeaturedEvents, }) { // 最初のレンダリングと同時にデータをロードする + useEffect(() => { + onLoadEvents('1111', 10, 15, 'keywords'); + onLoadFeaturedEvents('1111', 10, 15); + }, []); return ( <div> <Helmet> <title>EventPage</title> <meta name="description" content="Description of EventPage" /> </Helmet> <FormattedMessage {...messages.header} /> {/* データを表示 */} + <h4>Featured Events</h4> + {featuredEvents.map(e => ( + <p key={`event-${e.id}`}>{JSON.stringify(e)}</p> + ))} + <h4>Events</h4> + {events.map(e => ( + <p key={`featuredEvent-${e.id}`}>{JSON.stringify(e)}</p> + ))} </div> ); } ... // データとディスパッチのタイプを設定 EventPage.propTypes = { + events: PropTypes.array, + featuredEvents: PropTypes.array, + onLoadEvents: PropTypes.func, + onLoadFeaturedEvents: PropTypes.func, }; // データをプロップに注入 const mapStateToProps = createStructuredSelector({ - eventPage: makeSelectEventPage(), + featuredEvents: makeFeaturedEventsSelector(), + events: makeEventsSelector(), }); // ディスパッチをプロップに注入 function mapDispatchToProps(dispatch) { return { - dispatch, + onLoadEvents: (userId, skip, take, keywords) => + dispatch(loadEvents(userId, skip, take, keywords)), + onLoadFeaturedEvents: (userId, skip, take) => + dispatch(loadFeaturedEvents(userId, skip, take)), }; } ... テストしてみる まずは新しいページが表示されるようにApp内でルーティングを設定します app/containers/App/index.js + import EventPage from 'containers/EventPage/Loadable'; <Switch> <Route exact path="/" component={HomePage} /> <Route path="/features" component={FeaturePage} /> + <Route path="/events" component={EventPage} /> <Route path="" component={NotFoundPage} /> </Switch> これで完了!npm run startでhttp://localhost:3000/eventsを開いて確認しましょう!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[React Native] ScrollView, FlatList使用時のwarning修正

WARN VirtualizedLists should never be nested inside plain ScrollViews with the same orientation - use another VirtualizedList-backed container instead. 原因 ScrollViewの中にflatListを入れている 解決策 リストと一緒にスクロールしたいコンポーネントは flatList の以下の props に渡す。 ・ ListHeaderComponent ・ ListFooterComponent 参考文献 以下の情報を参考にさせていただきました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

KintoneカスタマイズをReact+webpackでやる

やろうと思ったきっかけ 複数アプリで同じような記述が増えていき、いろいろと共通化したかった(これが一番) 上記に似ているが、複数アプリに同じファイルを適用していると、変更時に差し替えるアプリを把握しきれなかった&その作業時間が無駄だなと感じた 素のjavascriptで書くのに疲れた&Reactを使いたかった 導入時にしたこと 目指せ!JavaScriptカスタマイズ中級者(1) 〜webpack編〜 (CDN) を参考にしました npmでReactを入れる バージョンの相性があるみたいで、私は以下のバージョンで使っています(困ったのはこの辺だったと思う、、曖昧) package version react 16.8.6 react-dom 16.5.2 webpack 4.44.2 webpack-cli 3.3.12 webpack-dev-server 3.1.14 style-loader 2.0.0 css-loader 5.2.6 babelrcの設定 正直よく分かってないけどReact使うにはpresetsをこうしないといけないっぽい。(babelrcを理解してない) .babelrc { "presets": [ [ "@babel/preset-react", { "targets": { "node": true } } ], [ "@babel/preset-env", { "targets": { "node": true } } ] ], "plugins": [ "@babel/plugin-proposal-class-properties" ] } これまで作ったモジュールの移行+αでやったこと src/commonに、大量データ処理用APIを置いた。 GETは レコード一括取得の JavaScript コーディング例:カーソル API を利用する方法(CDN) を参考に POST/PUT/DELETEは 【kintone】上限を気にせずPOST, PUT, DELETEする を参考に kintone UI Component v0.8.0 を入れた v1系はReact非対応だそう。最初v1を入れてしまい、ちょっと悩みました kintone-spinnerを入れた ReactでFont Awesome 5を使用する方法 を参考に、Font Awesomeを入れた style-loaderとcss-loaderを入れた 感想 最初からこれでやればよかった。が、Kintoneを触り始めた時はこんな風にできるとは知らなかったので、Kintoneに慣れてきたこの時期にできたのは良かったと思います。 webpack導入前からcustomize-uploaderを使っていて、その時は保存したら自動アップロードしてくれていましたが、webpack入れてからは保存→npm run buildでビルド→自動アップロードという手順になるので、うっかりビルド忘れたら変更が反映されず、え?ってなることが多々あるので慣れるまでは気を付けないとです。 他にもエラー出たところたくさんあった気がしますが、もうよく覚えていないので、この辺で。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

コンポーネントからStoreのStateを変更するまでの流れ

はじめに 前回、react-reduxについて自分なりに記事を書きました。しかし実際にコンポーネントとstoreを繋いでstateを変更するのはどういう流れになっているのかさらに深堀するために今回はコードベースで書いてみようと思いました。 コンポーネント作成 それでは、早速やっていきましょう。 今回はボタンをクリックするとダイアログが発生する処理を書きます。そのダイアログが発生する箇所をstoreに記載します。 まずはstoreを繋ぐコンポーネント用意します。 今回は、material-uiボタンを置くだけの簡単なコンポーネントを作成します。 myComponent.tsx import React from 'react'; import * as patientList from '../../store/MyComponent'; import Fab from '@material-ui/core/Fab'; import AddIcon from '@material-ui/icons/Add'; import Button from '@material-ui/core/Button'; import AddCircleIcon from '@material-ui/icons/AddCircle'; return ( <React.Fragment> <Fab color='primary' style={{ position: 'fixed', zIndex: 3, right: '2em', bottom: '2em', }} onClick={onListClick} > <AddIcon /> </Fab> <Button onClick={() => { history.push('/test') //UDVコンポーネント }} > <AddCircleIcon color="secondary" /> </Button> <div style={{ height: 500, width: 600, }} > <List columns={mockColumn} items={mockItems} /> </div> </React.Fragment> ); export default MyComponent; これでブラウザの右下に以下のようなボタンが表示される storeの作成 次に上記で作成したコンポーネントに接続するstoreを作成します 今回実装したコードを表示します。 myComponent.ts import * as React from 'react'; import { Action, Reducer } from 'redux'; import { AppThunkAction } from '.'; import { push, RouterAction } from 'react-router-redux' export interface MyComponentState { master: Master; test: string; userData: any; }; interface Master { word: string } interface Init { type: 'MYCOMPONENT/INIT'; value: any; } interface GetMaster { type: 'MYCOMPONENT/SET_MASTER'; master: Master; } interface ClearAll { type: 'MYCOMPONENT/CLEAR_ALL'; } interface ListClick { type: 'MYCOMPONENT/LIST_CLICK'; } export type KnownAction = Init | GetMaster | ListClick | ClearAll; const unloadedState: MyComponentState = { master: { word: '' }, test: '', userData: {} }; export interface IActionCreators { onInit: () => AppThunkAction<KnownAction>; onClick: () => AppThunkAction<KnownAction | RouterAction>; setMaster: () => AppThunkAction<KnownAction>; }; export type ActionCreators = IActionCreators; function setInit() { return 'test'; } export const actionCreators: ActionCreators = { onInit: () => (dispatch, getState) => { const userData = setInit(); console.log(userData); dispatch({ type: 'MYCOMPONENT/INIT', value: userData }); }, onClick: () => (dispatch, getState) => { alert('onClick run'); dispatch(push('/case')); dispatch({ type: 'MYCOMPONENT/LIST_CLICK' }); }, setMaster: () => (dispatch, getState) => { dispatch({ type: 'MYCOMPONENT/SET_MASTER', master: { word: 'test' } }); } } export const reducer = (state: MyComponentState, incomingAction: Action) => { const action = incomingAction as KnownAction; switch (action.type) { case 'MYCOMPONENT/INIT': { return { ...state, userData: action.value } } case 'MYCOMPONENT/SET_MASTER': { return { ...state, master: action.master } } case 'MYCOMPONENT/LIST_CLICK': { return { ...state, } } case 'MYCOMPONENT/CLEAR_ALL': { return { ...unloadedState, } } default: const exhaustiveCheck: never = action; } return state || unloadedState; }; 今回は、ボタンをクリックすると、'onClick run'というアラートを表示させるのでまず初めにonClickというActionをAction Creatorに追加しました。 export interface IActionCreators { onInit: () => AppThunkAction<KnownAction>; onClick: () => AppThunkAction<KnownAction | RouterAction>; setMaster: () => AppThunkAction<KnownAction>; }; 次に、stateを変更するActionの中身を実装する。 export const actionCreators: ActionCreators = { onInit: () => (dispatch, getState) => { const userData = setInit(); console.log(userData); dispatch({ type: 'MYCOMPONENT/INIT', value: userData }); }, setMaster: () => (dispatch, getState) => { dispatch({ type: 'MYCOMPONENT/SET_MASTER', master: { word: 'test' } }); } } onClickというAction Creatorの中に処理の内容を実装します。 今回追加する処理はボタンをクリックすると'onClick run'というアラートを表示させるだけなので以下のような処理を追加します。 onClick: () => (dispatch, getState) => { alert('onClick run'); dispatch({ type: 'MYCOMPONENT/ON_CLICK' }); }, このままでは、Actionが発行されてもstateを変更できないので最後にReducerを以下ように定義します。 export const reducer = (state: MyComponentState, incomingAction: Action) => { const action = incomingAction as KnownAction; switch (action.type) { case 'MYCOMPONENT/INIT': { return { ...state, userData: action.value } } case 'MYCOMPONENT/SET_MASTER': { return { ...state, master: action.master } } case 'MYCOMPONENT/LIST_CLICK': { return { ...state, } } case 'MYCOMPONENT/CLEAR_ALL': { return { ...unloadedState, } } default: const exhaustiveCheck: never = action; } return state || unloadedState; }; これでstoreの実装は完成しました。 次に、このstoreをコンポーネントに接続する必要があります。 myComponent.tsx export default MyComponent; この箇所を修正してstoreに接続できるようにします。 connectを用いてstoreと接続します。 myComponent.tsx export default connect( (state: ApplicationState) => (state.patientList), (dispatch) => bindActionCreators({ ...myComponent.actionCreators }, dispatch) )(MyComponent); それでは動作確認を行います。 小さくですが、画面上部に'onClick run'というアラートが表示されたのでコンポーネントとstoreが接続できたことを確認できました。 まとめ コンポーネントとstoreの接続フローを最小単位のコードベースで説明しました。 今回は、ただアラートを表示させるだけの簡単な処理になりました、画面遷移やその他の処理でもこの方法で実装することができます。 この方法を取ることでstateの管理が楽になるのでこれからの開発でも使っていこうと思います!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

whereとorderByを同時に実行(React + firebase)

この記事を書いた理由 Reactを学習し始めて2ヶ月程度。 なんとなくでしかまだわかっていないのですが、とにかく実践あるのみかと思い、家計簿アプリを作成しました。 結果つまずいたところがたくさんあったので、備忘録がてら記事にしたいと思います。 また、私と同じくReactの学習を始めたての方の参考になれば嬉しいです。 本題:表示しているデータが月ごと、ユーザーごとでソートできない 家計簿アプリなので、例えば2021年7月に入力したデータは7月のページで表示させたいし 2021年6月に入力したデータなら、2021年6月に表示させたい。 これは下記のソースコードで、ログインしているユーザーが入力したデータ(uidを取得)と、 入力した日付(date)でソートをかけているのですが Home.js const getIncomeData = () => { const incomeData = db.collection('incomeItems'); incomeData .where('uid', '==', currentUser.uid) .orderBy('date') .startAt(startOfMonth(date)) .endAt(endOfMonth(date)) .onSnapshot((query) => { const incomeItems = []; query.forEach((doc) => incomeItems.push({ ...doc.data(), docId: doc.id }) ); setIncomeItems(incomeItems); }); }; なぜか入力していたデータが一つも表示されません。 色々調べていると、whereとorderByを同時に実行したい場合は firebase側で設定をしないといけないようでした、 参考記事:Flutter: FirestoreでwhereとorderByを同時に実行したい これに気づかず、whereとorderByの組み合わせがダメなのか?と色々試行錯誤して かなり沼にハマりました・・・泣 要は、「uid」を取得してなおかつ、「date」を取得して並び替えるのであれば、 firebase側でデータを取得してソートするのであれば、複合インデックスを作らないといけないようです。 コンソールのエラーにも出ていました。。 コンソールのエラーにはURLが出ていて、それをクリックすると自動で必要なインデックスを作成してくれるようなのですが、なぜか私のアプリではURLなんて出てくれなかったので、自分でインデックス作りました◎ インデックスを設定したら、無事データが出てくれるようになりました〜!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ログイン後、リロードすると一瞬ログイン画面が表示される(React + firebase)

この記事を書いた理由 Reactを学習し始めて2ヶ月程度。 なんとなくでしかまだわかっていないのですが、とにかく実践あるのみかと思い、家計簿アプリを作成しました。 結果つまずいたところがたくさんあったので、備忘録がてら記事にしたいと思います。 また、私と同じくReactの学習を始めたての方の参考になれば嬉しいです。 本題:ログイン後、リロードすると一瞬ログイン画面が表示される ログインをしているかどうかを判別し、 ログインしているのであればHomeコンポーネントを表示 そうでなければLoginコンポーネントを表示する といった挙動にしていました。 App.js function App() { return ( <AuthProvider> <Router> <div> <PrivateRoute exact path="/" component={Home} /> <Route exact path="/Signup" component={Signup} /> <Route exact path="/Login" component={Login} /> </div> </Router> </AuthProvider> ); } export default App; これが、ログインしているはずなのに、ほんの一瞬ですがLoginコンポーネントが表示されていました。 これは、firebaseのデータベースに登録されているユーザーなのかどうかを取得するまでの間に 先にログインしていない認識の状態のLoginコンポーネントが出てしまっていたようです。 これは一旦、ログインをしているかしていないかの判断を待っている間の ローディング画面を作ってあげる必要があります。 まずuseStateで下記内容を作成。 AuthProvider.js const [loading, setLoading] = useState(true); 初期値はtrue(ローディング画面の状態)にしておきます。 その後、ログイン認証済かどうか確認する前に下記useEffectの記述を追加。 AuthProvider.js useEffect(() => { const unsubscribed = auth.onAuthStateChanged((user) => { setCurrentUser(user); setLoading(false); }); return () => { unsubscribed(); }; }, []); userがログインしていると無事判別できたら、 ローディングをfalseにすることで、Homeコンポーネントが表示されるようになりました!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React】Identifier 'Hoge' has already been declaredの対処法【javascript】

症状 Reactで画面作成時に以下のエラーが発生しました。 翻訳すると、「識別子 'Hoge'はすでに宣言されています」でした。 シンプルに、変数が二重に宣言されていると言われているようです。 エラー Identifier 'Hoge' has already been declared 以下が該当のソースです。 HogeHome.jsx import React,{useState} from "react"; import Hoge from "../Hoge"; import Hoge from "../Hoge"; export const HogeHome = () => { const [value, setValue] = useState(Hoge); return ( {value} ) } 解決策 imortが2重に書かれていた箇所を1つに直したら、解決しました。 importした値やオブジェクトが2重にimportされているため、既に宣言されているというエラーが出ていたようです。 HogeHome.jsx import React,{useState} from "react"; import Hoge from "../Hoge"; import Hoge from "../Hoge"; export const HogeHome = () => { const [value, setValue] = useState(Hoge); return ( {value} ) } 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

useContextを使っていて、Cannot destructure property 'pause' of 'Object(...)(...)' as it is undefined.のエラーがでたとき

useContextを使っているときにこんなエラーが出ました。 Cannot destructure property 'pause' of 'Object(...)(...)' as it is undefined. オブジェクトでのpauseが未定義だと言われてしまいました。解決するには本当にエラー文そのままで、pauseの型を考えて上げればいいと思います。 そこで、pauseがあるところまで戻ってみました。 \\import React,{createContext} from 'react' export const Store = createContext() const App = () => { const [pause, setPause] = useState(false) return( ... <Store.Provider value={{pause, setPause}}> .......... </Store.Provider> ... ) } そこまで問題がないように見えます。 実は、ここではなくて、importするときにpauseの型が適切ではありませんでした。 import Store from './App' これを import {Store} from './App' にしてあげる解決しました。初歩的過ぎますね。オブジェクトの形でStoreに保存しているのにimportする形はオブジェクトでなかったため怒られていたようです。 日本語の記事がなかったため、書いてみました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React - 学ぶべきポイントまとめ【フロントエンド開発入門者向け】

こんにちは! 本記事は React を始める方向けに、必要な学習ポイント、および、参考になるページを紹介します! 本記事では、詳細の解説はしていませんが、以降の連載で解説してきたいと思いますので、良ければ閲覧ください。 学習ポイント、参考ページの紹介 React 公式チュートリアル JSX State Props State と Props の違い 関数コンポーネントとクラスコンポーネント フック Redux バリデーション テスト Ignite UI for React トライアル版を利用するには Ignite UI for React はトライアル板での試用が可能です。 トライアルのダウンロードはこちら また、こちらのページ よりアカウントの作成を行っていただければ、実際にサブスクリプションをお持ちの場合と同じ内容のテクニカルサポートをご利用いただくことが出来ますのでぜひご利用ください。(初回利用より30日間) また、製品をご購入をご検討のお客様は こちらのページ よりお気軽にお問い合わせください。そのほか、製品デモや、弊社製品そのものに関するご相談だけでなく、システム開発における様々なご相談も実施可能な無料相談会も随時受け付けています。 開発全般に関するご相談はお任せください! インフラジスティックス・ジャパンでは、各開発プラットフォームの技術トレーニング、コンサルテーションの提供、開発全般のご支援を行っています。 「古い技術やサポート終了のプラットフォームから脱却する必要があるが、その移行先のプラットフォームやフレームワークの検討が進まない、知見がない。」 「新しい開発テクノロジーを採用したいが、自社内にエキスパートがいない。日本語リソースも少ないし、開発を進められるか不安。」 「自社のメンバーで開発を進めたいが、これまで開発フェーズを外部ベンダーに頼ってきたため、ツールや技術に対する理解が乏しい。」 「UIを刷新したい。UIデザインやUI/UXに関する検討の進め方が分からない。外部のデザイン会社に頼むと、開発が難しくなるのではないか、危惧している。」 といったご相談を承っています。 お問い合せはこちらからお気軽にご相談ください。 技術サポート・無料オンライン相談会をご利用ください インフラジスティックスのUI製品は多くの機能を備えているためドキュメントの情報量も多く、なかなかお探しの情報に辿り着けない場合もあります。そういった際はお気軽に技術サポートや、製品導入支援担当との無料オンライン相談会をご予約いただくことで検証時間を節約可能ですので、ぜひご活用ください。 技術サポートへの問い合わせ方法を確認する 無料オンライン相談会を予約する
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reactでcreate-react-appを使わずにwebpackでGAEにデプロイ【忘備録】

 Reactで作ったアプリをGAEにデプロイするまでを忘備録として残します。  create-react-appで作らずに「作りながら学ぶ React 入門」(吉田裕美,https://github.com/yuumi3/react_book)を参考に環境を作りました。 構成 ├── public │ └── index.html ├── src │ └── index.js ├── .babelrc ├── .eslintrc.json ├── app.yaml ├── package-lock.json ├── package.json └── webpack.config.js GAEにデプロイ 「ハンズオンNode.js」(今村謙士,p355〜360)を参考にGoogle Cloud SDKをインストールする。プロジェクトを作成する。 package.jsonのscriptsにbuildを追記する。 package.json "scripts": { "start": "webpack serve", "webpack": "webpack -d", "build": "webpack build" //buildを追記  app.yamlに「create-react-appを使わない構成でGAEにデプロイする」(@yjiro0403,https://qiita.com/yjiro0403/items/e0c743eb1f230c404fb0) をから引用させていただいたコードを記述する。  また、「ReactをGCPのGAEにデプロイする方法」 (@ozora,https://qiita.com/ozora/items/5808046b10cef887804a) よりnodejsのバージョンを自分のバージョンに変更して記述する。 app.yaml runtime: nodejs14 //数字は使っているバージョンにする。 handlers: - url: / static_files: public/index.html upload: public/index.html secure: always - url: / static_dir: public 下記のコマンドを実行する。 $ gcloud init //SDKのインストールの時に使ったのでここでは使わない可能性あり。 $ npm run build $ gcloud app deploy 下記のコマンドでデプロイができているか確認する。 $ gcloud app browse 参考 1.作りながら学ぶ React 入門 (吉田裕美,https://github.com/yuumi3/react_book) 2.ハンズオンNode.js(今村謙士,p355〜360) 3.「create-react-appを使わない構成でGAEにデプロイする」 (@yjiro0403,https://qiita.com/yjiro0403/items/e0c743eb1f230c404fb0) 4.「ReactをGCPのGAEにデプロイする方法」 (@ozora,https://qiita.com/ozora/items/5808046b10cef887804a)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

react-adminでSupabaseのユーザー認証を使う方法

Supabaseにはユーザー認証のAPIがあり、その認証システムをreact-adminで使う方法です。 authProviderを実装する react-adminには認証の仕組みがあり、これは好きな認証システムを使えます。自前の認証システムを使うには、Auth Providerを実装します。 なので、Supabaseの認証システムをreact-adminに組み込みたいときは、Supabaseの認証APIを使ったAuth Providerを作ることになります。 その実装は次のようになります: authProvider.ts import { AuthProvider } from "ra-core"; import { UserIdentity } from "react-admin"; import { supabase } from "./supabaseClient"; const authProvider: AuthProvider = { async login({ username, password }): Promise<void> { const { error } = await supabase.auth.signIn({ email: username, password }); if (error) throw error; }, async logout(): Promise<void | false | string> { const { error } = await supabase.auth.signOut(); if (error) throw error; }, async checkError(): Promise<void> {}, async checkAuth(): Promise<void> { const user = supabase.auth.user(); if (!user) throw undefined; }, async getPermissions(): Promise<any> {}, async getIdentity(): Promise<UserIdentity> { const user = supabase.auth.user(); if (!user) throw new Error("Failed to get user identity"); return { id: user.id, fullName: user.email }; }, }; export default authProvider; react-adminにAuth Providerを組み込む Auth Providerが実装できたら、あとはreact-adminの<Admin>コンポーネントのauthProviderパラメーターにそれを指定するだけです: import authProvider from './authProvider' <Admin dataProvider={dataProvider} authProvider={authProvider}>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ReactでTodo App 作ってみた2

今日はチェックボックスの選択とtodoの削除! 学んだこと Todoアプリの作成 ・Todoの完了状態を変更できるようにする チェックボックス ・全て完了にするボタンが動作できるようにする チェックボックス ・全て完了にするボタンの一括操作をできるようにする チェックボックス ・完了済みのTodoを削除できるようにする   ■用語 every  配列のメソッド 配列全てがtrueならtrue 配列全てがtrueじゃなければfalse filter 配列のメソッド 配列に対してfilterをかける filterの中で呼ばれた関数がtrueを返したやつだけ返す
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React】チェックボックスのチェックが付かない原因はeventにあった

この記事では、チェックが付かない問題は解決しますが、その操作によってなぜ解決されたのかは、わからないため解説していません。(わかったら追記していきます)。 チェックが付かないコード まずはチェックが付かなかったコードを以下に記載します。 //チェックボックスの処理 const switchCheckBox = (e) => { e.preventDefault(); const todoId = todo.id; const newTodos = todos.map((todo) => { if (todo.id === todoId) { return { id: todo.id, title: todo.title, checkFlug: !todo.checkFlug, }; } return todo; }); setTodo(newTodos); }; 処理の解説 onClickでswitchCheckBoxが呼ばれることでチェックが付きます。  switchCheckBox関数  todosというtodoを持つ配列をmap関数で回しています。その際、一致したidのチェックボックスのみがtrueになった配列をnewTodosとして新たに生成しています。それをsetTodo(useState)に引数として渡すことで再レンダリングが起きるので、チェックボックスが更新されるはずなのですが更新されませんでした。 todoがもつcheckFlugが更新されていないのかとDeveloperToolで確認したのですが、しっかりと更新されていてわけワカメでした。 解決 e.preventDefaultが必要なかったみたいです。 以下で解決でした。 //チェックボックスの処理 const switchCheckBox = () => { const todoId = todo.id; const newTodos = todos.map((todo) => { if (todo.id === todoId) { return { id: todo.id, title: todo.title, checkFlug: !todo.checkFlug, }; } return todo; }); setTodo(newTodos); }; 原因 e.preventDefault();についての理解ができていなかった。勉強します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React】Tensorflow.js を使用してフロントエンドだけで手書き文字推測アプリケーションを作成する

概要 現在、機械学習を勉強しようとしたときに、使用言語の第一候補にはほぼPythonが上がると思います。それほど機械学習 × Pythonは、フレームワークやドキュメント、参考書が充実しています。 では、作成したモデルを実際のアプリケーションでどのように運用すればよいのでしょうか? 考えられるのは、Pythonを実行できるアプリケーションサーバーを用意して、フロントエンドとなるアプリケーションからAPIでデータをやり取りする方法です。しかし、フロントエンドで推測や分類をするたびにサーバーとのやり取りが発生するため、応答速度や費用面でコストがかかります。 フロントエンドだけで、Pythonで作成した機械学習モデルを実行できれば、このようなコストからは解放されます。 本記事では、Kerasで作成したMNIST学習モデル(CNNモデル)を、フロントエンドだけで実行するアプリケーションを作成します。 ※ 多少殴り書きですが、ご容赦を。 環境 windows 10 VSCode 参考にさせて頂いた記事 参考にした記事です。 今回やりたいことは、この記事で完結しているので、開発の流れなど +α を説明したいと思います。 記事のソースコード MNIST学習モデルの作成 Python実行環境を構築して、Kerasを使ってMNIST学習モデルの作成します。 ※ Kerasとは、機械学習フレームワーク Tensorflowを使いやすしたラッパーフレームワークです。 開発環境構築として、ローカルに構築するパターンと、Colabを使用するパターンの2つを紹介します。 ローカルに構築する GPUを使用するので、お使いのPCにNVIDIA製のGPUを積んでいるを持っている方が対象です。 上記のmain.pyに記載しているソースは、MNISTのMPLモデルです。より精度の高いCNNモデルは、以下を参照してください。 Colabを使用する とくにこだわりがなければ、Colaboratoryを使うのがおすすめです。 デフォルトで、tensorflow-gpu、kerasパッケージが導入されていて、GPU実行環境も提供されています。(神) バージョンに気を使わなきゃいけない環境構築に悩まされないで済みます。 サンプルコード 学習モデルの保存 学習が終わったモデルをmodel.h5として保存します。 model.save('out/model.h5') 学習モデルファイルの変換 Tensorflow.js(JavaScript)でmodel.h5を扱うためには、jsonファイルに変換する必要があります。 変換にも、Pythonを使います。 ローカルで変換を行う場合は、専用の仮想環境を作ったほうがいいです。 Colabでも変換ができるので、model.h5の容量が大きくない場合は、こっちで済ますのが無難です。(サンプルコードに載せてます) 変換を行うと、学習モデルの構造を保存したmodel.jsonと、重みを保存したgroup1-shard1of1.binが出力されます。 重みファイルは、アプリケーション表示時の負荷軽減のために、容量によって自動的に分割されるみたいです。 MNIST学習モデルを React で使用する フロントエンドアプリケーションは、React(CRA)で作成します。使用言語はTypeScriptです。 コードの詳細は、以下を参考にしてください。 パッケージインストール 以下をインストールします。 npm i @tensorflow/tfjs MNIST学習モデルの使用手順 変換した学習モデルファイルは、publicフォルダ内に置きます。(ソースコードでは、public/assets/cnn) 推測までの手順は、以下です。 MNIST学習モデルをロードする canvasの手書きデータを28×28pxのグレースケールに変換する 変換した画像データをreshape、標準化(0~255 → 0~1)に変換する ロードしたモデルインスタンスを使用して推測する モデルのロード const loadedModel = await tf.loadLayersModel('./assets/cnn/model.json'); canvas画像の変換 /** * canvasデータを変換する * @returns 28×28のグレースケール画像データ */ const convertImageData = () => { if (!context) return; const inputWidth = 28; const inputHeight = 28; // resize const tmpCanvas = document.createElement('canvas').getContext('2d')!; tmpCanvas.drawImage(context.canvas, 0, 0, inputWidth, inputHeight); // convert grayscale let imageData = tmpCanvas.getImageData(0, 0, inputWidth, inputHeight); for (let i = 0; i < imageData.data.length; i += 4) { const avg = (imageData.data[i] + imageData.data[i + 1] + imageData.data[i + 2]) / 3; imageData.data[i] = imageData.data[i + 1] = imageData.data[i + 2] = avg; } return imageData; }; 推測 /** * 推測 * @param imageData 28×28のグレースケール画像データ * @returns 結果 */ const predict = (imageData: ImageData) => { if (!model) return; const score = tf.tidy(() => { // convert to tensor (shape: [width, height, channels]) const channels = 1; // grayscale let input = tf.browser.fromPixels(imageData, channels).toFloat(); // normalized input = input.div(tf.scalar(255)); // reshape input format (shape: [batch_size, width, height, channels]) input = input.expandDims(); // predict return model.predict(input); }); const result: number[] = []; const datas = Array.isArray(score) ? score[0].dataSync() : score.dataSync(); datas.forEach(d => result.push(d)); return result; }; 成果物 作成したアプリケーション ソースコード まとめ 数年前に機械学習の勉強をしていたときに、モチベーションを保てなくなったのが、「じゃあ、それをアプリケーションとしてどう表現するのか?」という自分の表現力(技術力)の無さでした。 それから時間は少し経ちましたが、フロントエンドを勉強して、このように形にできてひとまずフラストレーションが解消されました。 培ってきた技術が繋がっていくのは楽しいです。 Document
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む