- 投稿日:2020-11-18T22:57:21+09:00
TypeScriptでユーザー定義モデルを作成する時ってこんなんで良いんだよね?
とりあえず完全に備忘録です.
前提
Reactを使っているものとする.
クラスコンポーネントなのにsuper
してねぇとかそういうのはとりあえず抜きで超ざっくり.
ReactのFile Structureとか細かい部分には触れない.こんなんでいいはず
おそらく
type
かinterface
のどちらかになる.
ユーザー定義型を作るときに定石っぽい気がする…多分
interface
の定義users.tsinterface IUsers { id: string, name: string, age: int, weight: number, hobby: string, birth: string, created: Date, updated: Date }クラスコンポーネントで実装する
App.tsximport 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)
とか無闇に使わないよう,自戒のため.
TypescriptもReactも実業務で使った事ないのでもっと良いパターンがあるのかもしれない.
とはいえES6をわかっていないだけなのかReactをわかっていないのか,はたまたJavaScriptをわかっていないのか(全部わかっていない可能性も…)
んじゃ「JavaScriptからやり直せや」という感じですが,おじさんにはハードル高いっす.楽しいけど.
- 投稿日:2020-11-18T22:57:21+09:00
TypeScriptでユーザー定義型を作成する時ってこんなんで良いんだよね?
とりあえず完全に備忘録です.
前提
Reactを使っているものとするのですが,クラスコンポーネントなのに
super
してねぇとか細かいところは抜きでアルティメットざっくりです.こんなんでいいはず
おそらく
type
かinterface
のどちらかになる.
ユーザー定義型を作るときに定石っぽい気がする…多分
interface
の定義users.tsinterface IUsers { id: string, name: string, age: int, weight: number, hobby: string, birth: string, created: Date, updated: Date }クラスコンポーネントで実装する
App.tsximport 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)
とか無闇に使わないよう,自戒のため.
TypescriptもReactも実業務で使った事ないのでもっと良いパターンがあるのかもしれない.
とはいえES6をわかっていないだけなのかReactをわかっていないのか,はたまたJavaScriptをわかっていないのか(全部わかっていない可能性も…)
んじゃ「JavaScriptからやり直せや」という感じですが,おじさんにはハードル高いっす.楽しいけど.
- 投稿日:2020-11-18T22:45:56+09:00
[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, }) } />
- 投稿日:2020-11-18T21:47:14+09:00
【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.jsconst 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
もちろん、通常どおり相対パスによる指定も可能です。
- 投稿日:2020-11-18T21:36:50+09:00
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.tsximport 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.tsximport 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.tsximport 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ボタンを押す
→StatelessFunctionalComponent
とReactMemoComponent
のログが流れてくる
→StatelessFunctionalComponent
とReactMemoComponent
の両方が再描画されているDeep Equalの活用
- 比較関数を書く
- trueを返すとレンダリングしない
- falseを返すとレンダリングする
サンプルコードで動作確認
App.tsximport 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.tsximport 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
だけ再描画されている
→ReactMemoComponent
、DeepEqualMemoComponent
は再描画されていないMemoボタンを押す
→StatelessFunctionalComponent
とReactMemoComponent
のログが流れてくる
→StatelessFunctionalComponent
とReactMemoComponent
が再描画されている
→DeepEqualMemoComponent
は再描画されていない
→memoTrigger
も受け取っているが、obj
の変更を検知しない限り再レンダリングは行わないDeep Equal Memoボタンを押す
→StatelessFunctionalComponent
とDeepEqualMemoComponent
のログが流れてくる
→StatelessFunctionalComponent
とDeepEqualMemoComponent
が再描画されている
→ReactMemoComponent
は再描画されていない
- 投稿日:2020-11-18T21:36:50+09:00
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.tsximport 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.tsximport 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.tsximport 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ボタンを押す
→StatelessFunctionalComponent
とReactMemoComponent
のログが流れてくる
→StatelessFunctionalComponent
とReactMemoComponent
の両方が再描画されているDeep Equalの活用
- 比較関数を書く
- trueを返すとレンダリングしない
- falseを返すとレンダリングする
サンプルコードで動作確認
App.tsximport 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.tsximport 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
だけ再描画されている
→ReactMemoComponent
、DeepEqualMemoComponent
は再描画されていないMemoボタンを押す
→StatelessFunctionalComponent
とReactMemoComponent
のログが流れてくる
→StatelessFunctionalComponent
とReactMemoComponent
が再描画されている
→DeepEqualMemoComponent
は再描画されていない
→memoTrigger
も受け取っているが、obj
の変更を検知しない限り再レンダリングは行わないDeep Equal Memoボタンを押す
→StatelessFunctionalComponent
とDeepEqualMemoComponent
のログが流れてくる
→StatelessFunctionalComponent
とDeepEqualMemoComponent
が再描画されている
→ReactMemoComponent
は再描画されていない
- 投稿日:2020-11-18T21:01:35+09:00
useStateでstateを変更しているのにリアルタイムに再描画されない
- 投稿日:2020-11-18T20:45:42+09:00
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> ); }
- 投稿日:2020-11-18T19:51:36+09:00
React-親から子に値を渡す方法【関数コンポーネント】
Propsって?
- 親コンポーネントから渡されたプロパティ(値)
- 直接外部のStateに接続する場合にはPropsで受け取る
- 不変のデータ
- 変更不可
- 親から渡される
Props は、一般的に、親コンポーネントから子コンポーネントに渡される値。
記述方法
今回説明するページの構造
OyaComponent.jsx - ChildComponent.jsx親Componentに値を管理するStateがある。
そのStateの値を子Componentで表示させる。では早速。
親Component
OyaComponent.jsximport 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.jsximport React from 'react'; function ChildComponent(props) { return ( //それぞれに値1,値2が表示される <p>{props.data.nickname}</p> <p>{props.data.txtData}</p> ) }渡された値の確認方法
consol.log(props);
- 投稿日:2020-11-18T19:30:55+09:00
バズったTwitterの動画、画像を自動収集するWebサイト
概要
twitterでバズった動画や画像のツイートをいいね順、リツイート順、日付順で閲覧できるwebサイトを作った
https://twipop-app.tontonton.work/使用した技術
サーバサイド
Railsフロントエンド
ReactAPI
公式TwitterAPIDB
firebase Realtime Databaseホスティング
Herokussl
cloud flare全体像
なぜRails
サーバサイドはRailsしか扱ったことがないから。Rails以外の技術を勉強する時間が勿体無い。
なぜReact
前々からSPAに興味がありReactの勉強を進めていた。Vueはちょっと勉強して挫折。
ページの部分更新するにもstate更新するだけで実現できてお手軽なのが魅力的。
gemはreact-rails[Reactを知る前の貧弱な僕]
ビューにpartialファイル用意して、コントローラで専用のインスタンス変数用意して
あとpartialを返すようにして、それとそれとjavascript側でDOM更新してっアワワワ⬇️
[Reactを知った頑強な僕]
「フンッ」取得データでstate更新
シャッ(華麗にページ更新される音)なぜRealtime Database
なるべく無料DBを使用したいと思っていた。
最初はFireBaseのCloud Fire Storeを使おうとしていた。Cloud Fire Storeについて
CRUDについて無料プランは以下の制限がある。
書き込み 2 万/日
読み取り 5 万/日
削除 2 万/日nosqlは初めて扱った。RDBしか使ったことのない僕は最初
「1日でこんなにselectしないでしょw」
とか見当違いのことを思っていた。実装を進めるにつれ、使用状況がおかしいことに気づいた。
どうやらこの制限はデータ件数のことみたいだった。データ件数自体は1万も2万も扱うつもりはないが、読み取るたびに累積していくので断念。
RealTime DataBaseについて
RealTime DataBaseはCRUDの制限はない。容量は1GB。
gemはfirebase。
今の所問題なく動いているが以下のようなデメリットもある。
- 日本語の関連記事があまりない
- データの降順機能がない。今の所、昇順で取得したリストを自力で降順するしかない
- FireBase自体はCloud FireStoreを推奨している
なぜHeroku
アクセス数などの制限が無くRailsと親和性の高いHerokuを使用することにした。
最初はFireBaseでホスティングしようと思っていたけど、Twitter APIを使ってデータ取得するのにNode.jsが必要なので断念。
FireBaseでNode.js利用するときはCloud Functionを使う必要がある。
Cloud FunctionはBlazeプランに変えないと使用できない。
プランを変えたからといってすぐ課金されるわけではないけど、なるべく慎重にいきたい。まとめ
DBの差し替えとかHeroku環境変数の設定とか、結構つまづいたことが多かったが学びも多かった。
今後も少しずつ改良を加えていきたい
- 投稿日:2020-11-18T19:25:19+09:00
【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の概念について
- State: データを保持する領域
ActionCreator: Actionオブジェクトを生成する関数
Action(object): Actionの情報をもとにステート更新を行う。Storeの「dispatchメソッド」でStoreにActionオブジェクトを渡す
※thunkなどで非同期を実装する際にはobjではなく関数をStoreに渡すReducer: Actionと現在のステート情報をもとに新しいステートを生成して、Storeに保持する
Store: ステートをもつ。store周りのメソッドをもつ。
Vueの概念について
- State: データを保持する領域
Getters: ステートに加工したデータを返す機能(同期処理only)
Mutations: ステートの変更を行う機能
Actions: 内部でMutationを行いStateを更新する機能(同期処理&非同期処理)
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がなかったりします。
翻訳作業時に足りない機能などは、同等のものを自作などする必要があります。
- 投稿日:2020-11-18T15:04:07+09:00
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.jsximport 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.jsximport 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を表示するようにします ) };この状態で、カウントアップされているのが確認できます
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メソッドを使っての実装ができました
- 投稿日:2020-11-18T14:43:29+09:00
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} /> ...
- 投稿日:2020-11-18T13:05:24+09:00
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は使いたくないのが本音だけど、
現場によっては使っているので対応が必要
- 投稿日:2020-11-18T02:15:25+09:00
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データ
注意ポイント
reqest.data
とすると、一番はじめの要素がルートというか頭の部分になるのだけど、renderするときにpod.items.metadata.name
みたいなことをやってもエラーになったrequest.data.items
とすると、一番はじめの要素がmap形式になるようで、renderするときにその次の要素から指定すればエラーはでなかった- つまるところ、stateにいれる値はmap型の階層まで定義してあげて、その部分から下の要素を普通に指定してあげればいいみたい
- そのため、取得したJSONデータの中に複数の層にまたがって必要なデータがある場合、stateを複数定義してあげてmap形式の階層で定義してあげればよいみたい
- いまいちなんでかはわからない