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

TypeScriptでユーザー定義モデルを作成する時ってこんなんで良いんだよね?

とりあえず完全に備忘録です.

前提

Reactを使っているものとする.
クラスコンポーネントなのにsuperしてねぇとかそういうのはとりあえず抜きで超ざっくり.
ReactFile Structureとか細かい部分には触れない.

こんなんでいいはず

おそらくtypeinterfaceのどちらかになる.
ユーザー定義型を作るときに定石っぽい気がする…多分

interfaceの定義

users.ts
interface IUsers {
    id: string,
    name: string,
    age: int,
    weight: number,
    hobby: string,
    birth: string,
    created: Date,
    updated: Date
}

クラスコンポーネントで実装する

App.tsx
import React, {Component} from 'react';
import { IUsers } from '../models/users';
import axios from 'axios';
...

interface IState {
    users: IUsers[]
}
class App extends Component<{}, IState>{
  // 読み取り用のstateとする(superとかctorとかはここは触れないこととする)
  readonly state = users: IState = {
    user : [],
  }
  componentDidMount() {
    // なんかaxiosとかでGetしてるのなら
    axios.get<IUser[]>('http://localhost:5000/api/users')
      .then((response) => {
        this.setState({
          users: response.data
        })
      })
  }
  render() {
    return (
      <div>
        <Header as='h2'>
          <Icon name='users' />
          <Header.Content>Cont Users</Header.Content>
        </Header>
        <List>
          {this.state.activities.map((users) => (
            <List.Item key={users.id}>{users.name}</List.Item>
          ))}
        </List>
      </div>
    );
  }
}

linterで弾くのが良いとは思うのですが,(users: any)とか無闇に使わないよう,自戒のため.
TypescriptReactも実業務で使った事ないのでもっと良いパターンがあるのかもしれない.
とはいえES6をわかっていないだけなのかReactをわかっていないのか,はたまたJavaScriptをわかっていないのか(全部わかっていない可能性も…)
んじゃ「JavaScriptからやり直せや」という感じですが,おじさんにはハードル高いっす.楽しいけど.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TypeScriptでユーザー定義型を作成する時ってこんなんで良いんだよね?

とりあえず完全に備忘録です.

前提

Reactを使っているものとするのですが,クラスコンポーネントなのにsuperしてねぇとか細かいところは抜きでアルティメットざっくりです.

こんなんでいいはず

おそらくtypeinterfaceのどちらかになる.
ユーザー定義型を作るときに定石っぽい気がする…多分

interfaceの定義

users.ts
interface IUsers {
    id: string,
    name: string,
    age: int,
    weight: number,
    hobby: string,
    birth: string,
    created: Date,
    updated: Date
}

クラスコンポーネントで実装する

App.tsx
import React, {Component} from 'react';
import { IUsers } from '../models/users';
import axios from 'axios';
...

interface IState {
    users: IUsers[]
}
class App extends Component<{}, IState>{
  // 読み取り用のstateとする(superとかctorとかはここは触れないこととする)
  readonly state = users: IState = {
    user : [],
  }
  componentDidMount() {
    // なんかaxiosとかでGetしてるのなら
    axios.get<IUser[]>('http://localhost:5000/api/users')
      .then((response) => {
        this.setState({
          users: response.data
        })
      })
  }
  render() {
    return (
      <div>
        <Header as='h2'>
          <Icon name='users' />
          <Header.Content>Cont Users</Header.Content>
        </Header>
        <List>
          {this.state.activities.map((users) => (
            <List.Item key={users.id}>{users.name}</List.Item>
          ))}
        </List>
      </div>
    );
  }
}

linterで弾くのが良いとは思うのですが,(users: any)とか無闇に使わないよう,自戒のため.
TypescriptReactも実業務で使った事ないのでもっと良いパターンがあるのかもしれない.
とはいえES6をわかっていないだけなのかReactをわかっていないのか,はたまたJavaScriptをわかっていないのか(全部わかっていない可能性も…)
んじゃ「JavaScriptからやり直せや」という感じですが,おじさんにはハードル高いっす.楽しいけど.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[React]デフォルトでオブジェクトを渡す関数における引数の受け取り方

以下のonSortEndにおいて引数を渡したいときはどうすれば良いでしょうか。

const Sample =()=> {
 //省略
  function onSortEnd({ oldIndex, newIndex }) {
    console.log(value);
  }

  render() {
 //省略
    <SortableList
                items={value}
                onSortEnd={onSortEnd}
              />
  }
}

結論

やることは二つ。

  • 分割代入だからonSortEndの呼び出しでオブジェクトを渡す
  • コールバック関数の引数で引数を受け取る
function onSortEnd({ oldIndex, newIndex, category }) {
    console.log(category);
  }

<SortableList
                items={value}
                onSortEnd={({ oldIndex, newIndex }) =>
                  onSortEnd({
                    oldIndex: oldIndex,
                    newIndex: newIndex,
                    category: category,
                  })
                }
              />
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【ReactNative・TypeScript】import に絶対パスを使用する設定

はじめに

ReactNative(0.63~) + TypeScript(4.0~)を使用するプロジェクトで
importに絶対パスを使用する方法をまとめます。

絶対パスによるインポートを許可することで、ネストの深いモジュールをインポートする場合も簡潔にパスを記述できます。

相対パスの例
import * from '../../../component/foo';
絶対パスの例
import * from 'src/component/foo';

方法

以下2つのファイルに設定を加えます。

  • tsconfig.json
  • metro.config.js

ReactNativeだとtsconfigだけではうまく動かない。

tsconfig.json
"baseUrl": "./"
metro.config.js
const path = require("path")

module.exports = {
  resolver: {
    extraNodeModules: {
      "src": path.resolve(__dirname, 'src'),
    }
  },
  transformer: {
    getTransformOptions: async () => ({
      transform: {
        experimentalImportSupport: false,
        inlineRequires: false,
      },
    }),
  },
}

まとめ

以下2つのファイルに設定を加えることで実現できる。

  • tsconfig.json
  • metro.config.js

もちろん、通常どおり相対パスによる指定も可能です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React.memoで無駄な再レンダリングを行わないようにする方法

Reactの木構造とrender

  • stateが変わると再レンダリングされる
  • 親のstateが変わると全ての子が再レンダリングされる

React.memoの活用

  • 親のstateが変わっても子に渡すpropsの値に変化がなければ再レンダリングしない

React.memoの基本構文

  • コンポーネントごとに設定する
import React, {FC} from 'react';

interface Props {
  trigger: number
}

// React.memo()でラップする
const MemoChild: FC<Props> = React.memo(({trigger}) => {
  // なんらかの重い処理
  return (
    <div>Memo Component: {trigger}</div>
  )
})

export default MemoChild;

サンプルコードで動作確認

https://github.com/shtwangy/react-memo-test

App.tsx
import React, {FC, useState} from 'react';
import {SfcChild, MemoChild, DeepEqualMemoChild} from "./components";

export interface Obj {
  deepTrigger: number
}

const App: FC = () => {
  const [sfcTrigger, setSfcTrigger] = useState<number>(0)
  const [memoTrigger, setMemoTrigger] = useState<number>(0)

  const countUpSfc = () => {
    setSfcTrigger(prevState => prevState + 1)
  }

  const countUpMemo = () => {
    setMemoTrigger(prevState => prevState + 1)
  }

  return (
      <div>
        <h2>React Memo Test</h2>
        <button onClick={countUpSfc}>SFC</button>
        <button onClick={countUpMemo}>Memo</button>

        <SfcChild trigger={sfcTrigger} />
        <MemoChild trigger={memoTrigger} />
      </div>
  );
};

export default App;
src/components/StatelessFunctionalComponent.tsx
import React, {FC} from 'react';

interface Props {
    trigger: number
}

const StatelessFunctionalComponent: FC<Props> = ({trigger}) => {
    const startTime = performance.now()

    for (let i=0; i < 10000; i++) {
        // 16桁の文字列を乱数生成
        const S="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
        const N=16
        Array.from(Array(N)).map(()=>S[Math.floor(Math.random()*S.length)]).join('')
    }

    const endTime = performance.now()

    console.log(`SFC: ${endTime - startTime} milliseconds`)

    return (
        <div>Stateless Functional Component: {trigger}</div>
    );
};

export default StatelessFunctionalComponent;
src/components/ReactMemoComponent.tsx
import React, {FC} from 'react';

interface Props {
    trigger: number
}

const ReactMemoComponent: FC<Props> = React.memo(({trigger}) => {
    const startTime = performance.now();

    for (let i=0; i < 10000; i++) {
        // 16桁の文字列を乱数生成
        const S="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
        const N=16
        Array.from(Array(N)).map(()=>S[Math.floor(Math.random()*S.length)]).join('')
    }

    const endTime = performance.now()

    console.log(`Memo: ${endTime - startTime} milliseconds`)

    return (
        <div>React Memo Component: {trigger}</div>
    )
})

export default ReactMemoComponent;

コンソールログを確認しながら、各ボタンを押して動作を確認する。
SFCボタンを押す
StatelessFunctionalComponentのログだけ流れてくる
StatelessFunctionalComponentだけ再描画されている(ReactMemoComponentは再描画されていない)

Memoボタンを押す
StatelessFunctionalComponentReactMemoComponentのログが流れてくる
StatelessFunctionalComponentReactMemoComponentの両方が再描画されている

Deep Equalの活用

  • 比較関数を書く
  • trueを返すとレンダリングしない
  • falseを返すとレンダリングする

サンプルコードで動作確認

App.tsx
import React, {FC, useState} from 'react';
import {SfcChild, MemoChild, DeepEqualMemoChild} from "./components";

export interface Obj {
  deepTrigger: number
}

const App: FC = () => {
  const [sfcTrigger, setSfcTrigger] = useState<number>(0)
  const [memoTrigger, setMemoTrigger] = useState<number>(0)
  const [obj, setObject] = useState<Obj>({deepTrigger: 0})

  const countUpSfc = () => {
    setSfcTrigger(prevState => prevState + 1)
  }

  const countUpMemo = () => {
    setMemoTrigger(prevState => prevState + 1)
  }

  const countUpDeepTrigger = () => {
    setObject(prevState => {
      return {deepTrigger: prevState.deepTrigger + 1}
    })
  }

  return (
      <div>
        <h2>React Memo Test</h2>
        <button onClick={countUpSfc}>SFC</button>
        <button onClick={countUpMemo}>Memo</button>
        <button onClick={countUpDeepTrigger}>Deep Equal Memo</button>

        <SfcChild trigger={sfcTrigger} />
        <MemoChild trigger={memoTrigger} />
        <DeepEqualMemoChild trigger={memoTrigger} obj={obj}/>
      </div>
  );
};

export default App;
src/components/DeepEqualMemoComponent.tsx
import React, {FC} from 'react';
import {Obj} from "../App";

interface Props {
    trigger: number
    obj: Obj
}

const DeepEqualMemoComponent: FC<Props> = React.memo(({trigger, obj}) => {
    const startTime = performance.now()

    for (let i=0; i < 10000; i++) {
        // 16桁の文字列を乱数生成
        const S="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
        const N=16
        Array.from(Array(N)).map(()=>S[Math.floor(Math.random()*S.length)]).join('')
    }

    const endTime = performance.now()

    console.log(`Deep Equal Memo: ${endTime - startTime} milliseconds`)

    return (
        <div>Deep Equal Memo Component: {trigger}</div>
    )
}, (prevProps: Props, nextProps: Props) => {
    const prevDeepTrigger = prevProps.obj.deepTrigger
    const nextDeepTrigger = nextProps.obj.deepTrigger
    return (prevDeepTrigger === nextDeepTrigger)
})

export default DeepEqualMemoComponent;

SFCボタンを押す
StatelessFunctionalComponentのログだけ流れてくる
StatelessFunctionalComponentだけ再描画されている
ReactMemoComponentDeepEqualMemoComponentは再描画されていない

Memoボタンを押す
StatelessFunctionalComponentReactMemoComponentのログが流れてくる
StatelessFunctionalComponentReactMemoComponentが再描画されている
DeepEqualMemoComponentは再描画されていない
memoTriggerも受け取っているが、objの変更を検知しない限り再レンダリングは行わない

Deep Equal Memoボタンを押す
StatelessFunctionalComponentDeepEqualMemoComponentのログが流れてくる
StatelessFunctionalComponentDeepEqualMemoComponentが再描画されている
ReactMemoComponentは再描画されていない

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React.memoで無駄な再レンダリングを行わないようにする

Reactの木構造とrender

  • stateが変わると再レンダリングされる
  • 親のstateが変わると全ての子が再レンダリングされる

React.memoの活用

  • 親のstateが変わっても子に渡すpropsの値に変化がなければ再レンダリングしない

React.memoの基本構文

  • コンポーネントごとに設定する
import React, {FC} from 'react';

interface Props {
  trigger: number
}

// React.memo()でラップする
const MemoChild: FC<Props> = React.memo(({trigger}) => {
  // なんらかの重い処理
  return (
    <div>Memo Component: {trigger}</div>
  )
})

export default MemoChild;

サンプルコードで動作確認

https://github.com/shtwangy/react-memo-test

App.tsx
import React, {FC, useState} from 'react';
import {SfcChild, MemoChild, DeepEqualMemoChild} from "./components";

export interface Obj {
  deepTrigger: number
}

const App: FC = () => {
  const [sfcTrigger, setSfcTrigger] = useState<number>(0)
  const [memoTrigger, setMemoTrigger] = useState<number>(0)

  const countUpSfc = () => {
    setSfcTrigger(prevState => prevState + 1)
  }

  const countUpMemo = () => {
    setMemoTrigger(prevState => prevState + 1)
  }

  return (
      <div>
        <h2>React Memo Test</h2>
        <button onClick={countUpSfc}>SFC</button>
        <button onClick={countUpMemo}>Memo</button>

        <SfcChild trigger={sfcTrigger} />
        <MemoChild trigger={memoTrigger} />
      </div>
  );
};

export default App;
src/components/StatelessFunctionalComponent.tsx
import React, {FC} from 'react';

interface Props {
    trigger: number
}

const StatelessFunctionalComponent: FC<Props> = ({trigger}) => {
    const startTime = performance.now()

    for (let i=0; i < 10000; i++) {
        // 16桁の文字列を乱数生成
        const S="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
        const N=16
        Array.from(Array(N)).map(()=>S[Math.floor(Math.random()*S.length)]).join('')
    }

    const endTime = performance.now()

    console.log(`SFC: ${endTime - startTime} milliseconds`)

    return (
        <div>Stateless Functional Component: {trigger}</div>
    );
};

export default StatelessFunctionalComponent;
src/components/ReactMemoComponent.tsx
import React, {FC} from 'react';

interface Props {
    trigger: number
}

const ReactMemoComponent: FC<Props> = React.memo(({trigger}) => {
    const startTime = performance.now();

    for (let i=0; i < 10000; i++) {
        // 16桁の文字列を乱数生成
        const S="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
        const N=16
        Array.from(Array(N)).map(()=>S[Math.floor(Math.random()*S.length)]).join('')
    }

    const endTime = performance.now()

    console.log(`Memo: ${endTime - startTime} milliseconds`)

    return (
        <div>React Memo Component: {trigger}</div>
    )
})

export default ReactMemoComponent;

コンソールログを確認しながら、各ボタンを押して動作を確認する。
SFCボタンを押す
StatelessFunctionalComponentのログだけ流れてくる
StatelessFunctionalComponentだけ再描画されている(ReactMemoComponentは再描画されていない)

Memoボタンを押す
StatelessFunctionalComponentReactMemoComponentのログが流れてくる
StatelessFunctionalComponentReactMemoComponentの両方が再描画されている

Deep Equalの活用

  • 比較関数を書く
  • trueを返すとレンダリングしない
  • falseを返すとレンダリングする

サンプルコードで動作確認

App.tsx
import React, {FC, useState} from 'react';
import {SfcChild, MemoChild, DeepEqualMemoChild} from "./components";

export interface Obj {
  deepTrigger: number
}

const App: FC = () => {
  const [sfcTrigger, setSfcTrigger] = useState<number>(0)
  const [memoTrigger, setMemoTrigger] = useState<number>(0)
  const [obj, setObject] = useState<Obj>({deepTrigger: 0})

  const countUpSfc = () => {
    setSfcTrigger(prevState => prevState + 1)
  }

  const countUpMemo = () => {
    setMemoTrigger(prevState => prevState + 1)
  }

  const countUpDeepTrigger = () => {
    setObject(prevState => {
      return {deepTrigger: prevState.deepTrigger + 1}
    })
  }

  return (
      <div>
        <h2>React Memo Test</h2>
        <button onClick={countUpSfc}>SFC</button>
        <button onClick={countUpMemo}>Memo</button>
        <button onClick={countUpDeepTrigger}>Deep Equal Memo</button>

        <SfcChild trigger={sfcTrigger} />
        <MemoChild trigger={memoTrigger} />
        <DeepEqualMemoChild trigger={memoTrigger} obj={obj}/>
      </div>
  );
};

export default App;
src/components/DeepEqualMemoComponent.tsx
import React, {FC} from 'react';
import {Obj} from "../App";

interface Props {
    trigger: number
    obj: Obj
}

const DeepEqualMemoComponent: FC<Props> = React.memo(({trigger, obj}) => {
    const startTime = performance.now()

    for (let i=0; i < 10000; i++) {
        // 16桁の文字列を乱数生成
        const S="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
        const N=16
        Array.from(Array(N)).map(()=>S[Math.floor(Math.random()*S.length)]).join('')
    }

    const endTime = performance.now()

    console.log(`Deep Equal Memo: ${endTime - startTime} milliseconds`)

    return (
        <div>Deep Equal Memo Component: {trigger}</div>
    )
}, (prevProps: Props, nextProps: Props) => {
    const prevDeepTrigger = prevProps.obj.deepTrigger
    const nextDeepTrigger = nextProps.obj.deepTrigger
    return (prevDeepTrigger === nextDeepTrigger)
})

export default DeepEqualMemoComponent;

SFCボタンを押す
StatelessFunctionalComponentのログだけ流れてくる
StatelessFunctionalComponentだけ再描画されている
ReactMemoComponentDeepEqualMemoComponentは再描画されていない

Memoボタンを押す
StatelessFunctionalComponentReactMemoComponentのログが流れてくる
StatelessFunctionalComponentReactMemoComponentが再描画されている
DeepEqualMemoComponentは再描画されていない
memoTriggerも受け取っているが、objの変更を検知しない限り再レンダリングは行わない

Deep Equal Memoボタンを押す
StatelessFunctionalComponentDeepEqualMemoComponentのログが流れてくる
StatelessFunctionalComponentDeepEqualMemoComponentが再描画されている
ReactMemoComponentは再描画されていない

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

useStateでstateを変更しているのにリアルタイムに再描画されない

結論

一時変数にstateを入れて、配列やオブジェクトの場合は展開して新しい配列として扱いましょう。

let temp = { ...props.samples };

ダメなやり方

一時変数に入れてるから以下のようにすれば大丈夫でしょって思っても、これは参照渡しになっています。
同じオブジェクトをいじろうとしてもうまく行きません。新しい配列を作るために展開してあげる必要があります。

//props.samples={hoge:"hoge", fuga:"fuga"}
let temp = props.samples; 
setState(temp)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reduxの基本的な使い方 例付き

Reduxの基本的な書き方

1 actionの作成

actionは、取り扱いたいデータの概要を書いているイメージ。

 
//reducks/dots/action.jsx
export const ADD_DOT = "ADD_DOT";
export const add_dot = (dot) => {
return {
        type: ADD_DOT,
        payload: dot,
    };
};

※export const ADD_DOT の意味… type名である"ADD_DOTS"を変数化することで、type名をtypoしたときに、その旨がエラー文にでるようになる。

2 reducerの作成

actionで作ったデータをどう変更したいかを定義しているイメージ

//reducks/dots/reducer.jsx

import * as Actions from "./action";
import initialState from "../store/initialState"; //次に作る

export const DotReducer = (state = initialState, action) => {
    switch (action.type) {
        case Actions.ADD_DOT:
            return [...state, action.payload];
        default:
            return state;
    }
};

3 initialStateの定義

const initialState = []
export default initialState

このinitialStateが、reducerのstateの初期状態になる。

4 Storeの作成

import { createStore as reduxCreateStore, combineReducers } from "redux";
import { DotReducer } from "../dots/reducers";

export default function createStore() {
    return reduxCreateStore(
        combineReducers({
            dots: DotReducer,
        })
);
}

5 Storeの中身を全てで使えるように設定

import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { applyMiddleware, compose } from "redux";
import reduxThunk from "redux-thunk";
import App from "./App";
import createStore from "./redux/store/store";

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; //Redux DevToolsを使うために定義
export const store = createStore(composeEnhancers(applyMiddleware(reduxThunk)));

ReactDOM.render(
    < Provider store={store}>
        < App />
    < /Provider>,
    document.getElementById("root")

これでAppコンポーネントにネストされてるものすべてを含めて、 storeで管理している内容が使えるようになった!!いえい!?

6 Actionを使っていく

・actionを使いたいコンポーネントに移動
・useDispatchと、actionをimport
・useDispatchを定義
・actionを使いたいところで「dispatch(アクション名(propsとして渡したいデータ));」


import { useDispatch } from "react-redux";
import { add_dot } from "../../redux/dots/action";

//....

const MiniForm = () => {
    const dispatch = useDispatch();
    const onSubmit = (data) => {
                //...
        });
        dispatch(add_dot(data));
    };
    return (
        < form onSubmit={handleSubmit(onSubmit)}>
        //...
        < /form>
    );
};
export default MiniForm;

これでactionの中に引数に指定したデータ(今回ならdata)が渡る→そのactionに該当するreducerが走る→storeに保管される 、まで行われる

7 Storeに保管されたstateを使う

・useSelectorのimport
・ useSelector((state) => state.dots)を定義。dotsはreducerのexport名(Store参照)

dotというreducerに入っているstateが使えるようになった。
あとは使っていくのみ。(今回はmapで広げる)

import React from "react";
import { useSelector } from "react-redux";

export default function Base() {
    const dots = useSelector((state) => state.dots);
    console.log(dots); //これでstoreからやってきたdotsが見れる!
    return (
        < React.Fragment>
            < Header />
            {dots.map((dot) => {     //storeからもらってきたdotsを使ってます
                return < Dots dot={dot} />; 
            })}
            //...
            < Footer />
        < /React.Fragment>
    );
}

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React-親から子に値を渡す方法【関数コンポーネント】

Propsって?

  • 親コンポーネントから渡されたプロパティ(値)
  • 直接外部のStateに接続する場合にはPropsで受け取る
  • 不変のデータ
  • 変更不可
  • 親から渡される

Props は、一般的に、親コンポーネントから子コンポーネントに渡される値。

記述方法

今回説明するページの構造

OyaComponent.jsx
 - ChildComponent.jsx

親Componentに値を管理するStateがある。
そのStateの値を子Componentで表示させる。

では早速。

親Component

OyaComponent.jsx
import React, { useState } from "react";
import TextField from "@material-ui/core/TextField";

function OyaComponent (props) {

const [allData, SetAllData] = useState({
    nickname: "値1",
    txtData:"値2"
  });

  return (
    <ChildComponent data={allData}/>
  )
}

子Component

ChildComponent.jsx
import React from 'react';

function ChildComponent(props) {
  return (
    //それぞれに値1,値2が表示される
    <p>{props.data.nickname}</p>
    <p>{props.data.txtData}</p>
  )
}

渡された値の確認方法

consol.log(props);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

バズったTwitterの動画、画像を自動収集するWebサイト

概要

twitterでバズった動画や画像のツイートをいいね順、リツイート順、日付順で閲覧できるwebサイトを作った
https://twipop-app.tontonton.work/

使用した技術

  • サーバサイド
    Rails

  • フロントエンド
    React

  • API
    公式TwitterAPI

  • DB
    firebase Realtime Database

  • ホスティング
    Heroku

  • ssl
    cloud flare

全体像

こんな感じ
export_img.png

なぜRails

サーバサイドはRailsしか扱ったことがないから。Rails以外の技術を勉強する時間が勿体無い。

なぜReact

前々からSPAに興味がありReactの勉強を進めていた。Vueはちょっと勉強して挫折。
ページの部分更新するにもstate更新するだけで実現できてお手軽なのが魅力的。
gemはreact-rails

[Reactを知る前の貧弱な僕]
ビューにpartialファイル用意して、コントローラで専用のインスタンス変数用意して:sweat_drops:
あとpartialを返すようにして、それとそれとjavascript側でDOM更新してっ:dizzy_face:アワワワ

⬇️

[Reactを知った頑強な僕]
「フンッ:muscle_tone4:」取得データでstate更新
シャッ(華麗にページ更新される音)

なぜRealtime Database

なるべく無料DBを使用したいと思っていた。
最初はFireBaseのCloud Fire Storeを使おうとしていた。

Cloud Fire Storeについて

CRUDについて無料プランは以下の制限がある。
書き込み 2 万/日
読み取り 5 万/日
削除 2 万/日

nosqlは初めて扱った。RDBしか使ったことのない僕は最初

「1日でこんなにselectしないでしょw」
とか見当違いのことを思っていた。

実装を進めるにつれ、使用状況がおかしいことに気づいた。

ラッコ.jpeg
「うわ・・・私の読み取り数多すぎ?」

どうやらこの制限はデータ件数のことみたいだった。データ件数自体は1万も2万も扱うつもりはないが、読み取るたびに累積していくので断念。

RealTime DataBaseについて

RealTime DataBaseはCRUDの制限はない。容量は1GB。
gemはfirebase。
今の所問題なく動いているが以下のようなデメリットもある。

  • 日本語の関連記事があまりない
  • データの降順機能がない。今の所、昇順で取得したリストを自力で降順するしかない
  • FireBase自体はCloud FireStoreを推奨している

参考
FireBase公式料金プラン

なぜHeroku

アクセス数などの制限が無くRailsと親和性の高いHerokuを使用することにした。

最初はFireBaseでホスティングしようと思っていたけど、Twitter APIを使ってデータ取得するのにNode.jsが必要なので断念。
FireBaseでNode.js利用するときはCloud Functionを使う必要がある。
Cloud FunctionはBlazeプランに変えないと使用できない。
プランを変えたからといってすぐ課金されるわけではないけど、なるべく慎重にいきたい。

まとめ

DBの差し替えとかHeroku環境変数の設定とか、結構つまづいたことが多かったが学びも多かった。
今後も少しずつ改良を加えていきたい

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React/Vue】ReactReduxとVuex 概念の互換性

記事について

この記事は、駆け出しエンジニアの僕がVue→Reactの翻訳学習の際に、より理解を深めるために綴っている記事です。
Vueについてはあまり学習しておらず、Reactに重点を置いて学習をしています。

拙い部分があるかもしれませんが、ご指摘・ご意見がある際は是非お願いします。

概念

ReactReduxとVuexでの基本的な概念

ReactRedux: State / ActionCreator / Action / Reducer / Store
Vuex: State / Getters / Mutations / Actions / Store

それぞれの互換性について理解を深めて、React ⇆ Vueの翻訳に役立てる

Reduxの概念について

  1. State: データを保持する領域  
  2. ActionCreator: Actionオブジェクトを生成する関数

  3. Action(object): Actionの情報をもとにステート更新を行う。Storeの「dispatchメソッド」でStoreにActionオブジェクトを渡す
      ※thunkなどで非同期を実装する際にはobjではなく関数をStoreに渡す

  4. Reducer: Actionと現在のステート情報をもとに新しいステートを生成して、Storeに保持する

  5. Store: ステートをもつ。store周りのメソッドをもつ。

Vueの概念について

  1. State: データを保持する領域  
  2. Getters: ステートに加工したデータを返す機能(同期処理only)

  3. Mutations: ステートの変更を行う機能

  4. Actions: 内部でMutationを行いStateを更新する機能(同期処理&非同期処理)

  5. Store: 1~4を保持するオブジェクト

互換性

reduxで非同期を実装する場合は、適応したライブラリのインストールが必要

ReactRedux(x) / Vuex(y) State ActionCreator Action Reducer Store
State ほぼ同義 x x x x
Getters x x x x x
Mutations x x x 同様にstateの値を更新する x
Actions x 非同期のロジックはActionCreatorに記述される Actionは非同期の関数にもなる x x
Store x x x x stateを保持するが、異なる部分もある

まとめ

互換性について記事を書きましたが、VueとReactは似ている部分もありますが別のフレームワークなので、例としてReactにはGettersがなかったりします。
翻訳作業時に足りない機能などは、同等のものを自作などする必要があります。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reactを学ぶXI〜React Hooksその2〜

■ はじめに

タイトルについて記事にしました。
この記事で得る内容は以下の通りです。

・ Reactの基礎知識が増える
・ React Hooks 関数コンポーネントでもライフサイクルを扱う方法について

■ useEffect()メソッド

React Hooksでライフサイクルを作る時は、useEffect()メソッドを使います

・ useEffect()メソッドを使うメリット

・ Functional Componentで書くことができるので、記述量が減らせる
→ Class Componentと比べてソースコードがシンプルで見やすいので、書きやすさと保守性が上がる

・ コードを分かりやすくまとめることができる
→ useEffectメソッドの中で、機能毎にコードをまとめる事ができるので、コードの可視性が上がる

・ useEffect()の仕組み

useEffectメソッドの中に記述した処理は、レンダー毎に走ります
代替できるメソッドは、componentDidMount()componentDidUpdate()componentWillUnmount()です

・ useEffect()の使い方

いくつかパターンがありますので、パターン毎に説明します

パターン①:レンダー毎
// レンダー毎に処理が走る基本の型で、useEffect()内にCallback関数() => {}を書く

useEffect(() => {
    console.log('render!') // Callbackはレンダー毎に呼ばれる
    return () => {
        console.log('Unmounting!')  // returnするCallback関数はアンマウント時に呼ばれる(クリーンアップ関数)
    }
})
パターン②:マウント時のみ
// Class Commponentでは、componentDidMount()に書いていた処理を実行するパターン

useEffect(() => {
    console.log('render!')
}, [])  // useEffectメソッドの第二引数に空の配列を渡すと、最初の1回(マウント時)のみ処理が実行される

// 第二引数の配列の値を、前回レンダーと今回レンダーで比較し、変更があればCallback関数を実行する
パターン③:マウント&アンマウント
// ①と②の複合型で、マウント時及び、アンマウント時のみ実行する

useEffect(() => {
    console.log('render!')  // 通常のCallbackはマウント時のみ
    return () => {
        console.log('Unmounting!') // アンマウント時はreturn内のクリーンアップ関数が実行される
    }
},  []) // updating期間は配列の中身は空なので実行しない
パターン④:特定のレンダー時に関数を実行
// useStateメソッドと使われることが多い

const [limit, release] = useState(true); // limitというstateの初期値がtrueで、releaseという関数でlimitを変更する事ができる

useEffect(() => {
    console.log('render!') // limitの値が変わると処理が実行される
}, [limit]) // useEffectメソッドの第二引数の配列にlimitを指定し、前回のレンダーと今回のレンダーで値を比較して変わった時にCallback関数が呼ばれる

■ 例

前回は、Articleメソッドの中でuseStateを使って、公開状態の切り替えをしていました

Article.jsx
import React, { useState } from "React";
import LikeButton from "./LikeButton";

const Article = (props) => {
  const [isPublished, togglePublished] = useState(false);

  return (
    <div>
      <h2>{props.title}</h2>
      <label htmlFor="check">公開状態:</label>
      <input type="checkbox" checked={isPublished} id="check" onClick={() => togglePublished(!isPublished)} />
      {<LikeButton/>}
    </div>
  );
};

export default Article;

そして、Blog.jsxでライフサイクルを使った処理を、useEffectを使って代替します

LikeButton.jsx
import React, { useState, useEffect } from "React"; // useStateとuseEffectを宣言

const LikeButton = () => {
    count [count,counter] = useState(0); // useStateを使って、カウント用のstate(countというstateと、counter(変更用のメソッド))を作ります

    const countUp = () => {
        counter(count +1); // 上記のcounterメソッドを使って、countの値に+1して返す処理
    };

    useEffect(() => {
        document.getElementById("counter").addEventListener("click", countUp);  // counterというidを持つDOM要素にクリックした時、countUp変数を呼び出す
    }):

    return (
        <button id={"counter"}>いいね数: {count}</button> // いいね数はcountのstateを表示するようにします
    ) 
};

この状態で、カウントアップされているのが確認できます

カウントされる.gif

componentWillUnmount()の部分も実装していきます

LikeButton.jsx
// 中略

    useEffect(() => {
        document.getElementById("counter").addEventListener("click", countUp);
        return() => {document.getElementById("counter"}.removeEventListener("click", countUp); // componentWillUnmountの処理で、いいねボタンはマウントされた時にイベントが設定されて、アンマウントされる時は、イベントリスナーが解除される
    }):

// 中略

続けて残りのcomponentDidUpdate()も実装します

LikeButton.jsx
// 中略

    useEffect(() => {
        document.getElementById("counter").addEventListener("click", countUp);
        if (count >= 10) {
            counter(0) // componentDidUpdateの部分は、useEffectメソッドが毎回呼ばれるので、条件を書くだけでOKです
        }
        return() => {document.getElementById("counter"}.removeEventListener("click", countUp);
    }):

// 中略

これでuseEffectメソッドを使っての実装ができました

useeffectでライフサイクル.gif

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

reactでhooksを使っているコンポーネントの呼び出す時のエラー

発生したエラー

  • Warning: React has detected a change in the order of Hooks called by...
  • Error: Rendered more hooks than during the previous render.

原因

関数形式でコンポーネントを呼んでいた

hooksは使えない
return SomeComponent(someProps)

ちゃんとcreateElementをするか、jsxで記述する

createElementを使う
import {createElement} from 'react';
...
return createElement(SomeComponent, someProps)
...
jsxを使う
import React from 'react';
...
return <SomeComponent {...someProps} />
...
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React@16以降のref(createRef)でのtypescriptの書き方 備忘録

Reactv16以降のref

typescriptでrefを使う際に悩んだので備忘録として残す

昔の書き方

以前のrefはこんな感じに書いていた(多分)

export class SampleComponent extends React.Component<Props, State> {
  private inputElement: HTMLInputElement
  constructor(props: Props) {
    super(props);
    this.inputElement = ""
  }
  render(){
    return(
      <input ref={input => this.inputElement = input} />
    )
  }
}

最近の書き方

Reactv16以降での新しい createRef をしようした場合
RefObjectに型を入れることでチェックできる

import * as React from 'react';

export class SampleComponent extends React.Component<Props, State> {
  private inputElement: React.RefObject<HTMLInputElement>
  constructor(props: Props) {
    super(props);
    this.inputElement = React.createRef();
  }
  render(){
    return(
      <input ref={this.inputElement} />
    )
  }
}

まぁ、あんまりrefは使いたくないのが本音だけど、
現場によっては使っているので対応が必要

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ReactでJSONデータをmap型で扱うときの注意点

APIからJSONを取得

    componentWillMount(){
        console.log("getpod")
        const request = axios.create({
            baseURL: "http://127.0.0.1:8080",
            method: "GET"
        })

        request.get("/getpods")
            .then(request => {
                // console.log(request.data)
                this.setState({
                    pods: request.data.items
                })
                console.log(this.state.pods)
            })
    }

JSONの要素を取り出す

                    <div>
                        <ul>
                            {this.state.pods.map((pod) => {
                                return <li>{pod.metadata.name}  {pod.status.phase}</li>;
                            })}
                        </ul>
                    </div>

取得されたJSONデータ

image.png

注意ポイント

  • reqest.data とすると、一番はじめの要素がルートというか頭の部分になるのだけど、renderするときにpod.items.metadata.nameみたいなことをやってもエラーになった
  • request.data.itemsとすると、一番はじめの要素がmap形式になるようで、renderするときにその次の要素から指定すればエラーはでなかった
  • つまるところ、stateにいれる値はmap型の階層まで定義してあげて、その部分から下の要素を普通に指定してあげればいいみたい
    • そのため、取得したJSONデータの中に複数の層にまたがって必要なデータがある場合、stateを複数定義してあげてmap形式の階層で定義してあげればよいみたい
  • いまいちなんでかはわからない
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む