- 投稿日:2020-07-14T15:44:37+09:00
React.jsでemotionを使うときはJSX Pragmaをつける必要がありまして。
普通にimportしただけだと動かない
Emotion
というCSSフレームワークを使ってちょっとお試しで実装していました。
package.json
は以下の通りとなっております。{ "dependencies": { "@emotion/core": "^10.0.28", "next": "9.4.4", "react": "16.13.1", "react-dom": "16.13.1" }, "devDependencies": { "@types/node": "^14.0.22", "@types/react": "^16.9.43", "typescript": "^3.9.6" } }スタイルを設定したいページにEmotionをimportしたのですが・・・。
import { Global, css, jsx } from "@emotion/core";これだけだとスタイルが適応されないので調査いたしました。
どうやら下記のように@jsx jsx
がないとうまいことコンパイルされないようでした。/** @jsx jsx */ import { Global, css, jsx } from "@emotion/core";
jsx
をコンパイルする際は、Emotionは独自のコンパイラを通す必要があり、/** @jsx jsx */
というJSX Pragmaの記述をしないと動きませんでした。以下、名著「りあクト! TypeScriptで極める現場のReact開発」から引用
「Emotion はちょっと他のライブラリと違って特殊なことをしているので、使うにあたっていくつか注意事項があるのね。Emotion の文法を使って記述したJSXは通常React.createElement() ではなくEmotion が提供するコンパイラを通す必要があるの。」
「@emotion/core からインポートしてるjsx というのがそのコンパイラね。で、JSX リテラルで書いたコードをそれに渡すためにファイル冒頭で/** @jsx jsx */ の『JSX Pragma』というマジックワードを記述してあげてるの」
「えっ、言われるまで気づきませんでした。そうなんですね」
「さらに、これに起因する注意点が三つあるの。ひとつめは『親コンポーネントでJSX Pragma を使用したら、子のコンポーネントでもJSX Pragma を使わなければならない』こと」
「使わないとどうなるんですか?」
「『ReferenceError: React is not defined』って怒られてコンパイルに失敗するね。場合によっては直上の親コンポーネントにもJSX Pragma が必要なときもある。Presentational Component をインクルードしてContainer Component を作るときとか、それ自体がEmotion を使ってなくてもJSX Pragma を書かないとコンパイルが通らない」コンパイルエラーとありますが、私の環境ではエラーは出ずに、スタイルが当たらないという状況でした。
実装例
Emotionでは子コンポーネントでもJSX Pragmaの記述が必要ということで、
お試しとして、Indexからヘッダーを呼び出すものを実装してみました。↓こんな感じです。
src │ └─pages index.tsx header.tsxheader.tsx/** @jsx jsx */ import { Global, css, jsx } from "@emotion/core"; const globalCss = css` * { margin: 0; padding: 0; } `; const styles = { titleBase: css` height: 168px; width: 100%; `, title: css` height: 168px; width: 100%; font-family: Roboto; font-size: 64px; color: #ffffff; background: #2f80ed; `, }; interface Props { title: string; } const Header = (props: Props) => { return ( <> <Global styles={globalCss} /> <div css={styles.titleBase}> <h1 css={styles.title}>{props.title}</h1> </div> </> ); }; export default Header;index.tsximport Header from "./header"; /** @jsx jsx */ import { css, jsx } from "@emotion/core"; const styles = { content: css` height: 100%; width: 100%; font-size: 64px; `, }; export default function Index() { return ( <> <Header title="タイトル" /> <div css={styles.content}>Content</div> </> ); }Emotionをjsxで使いたい場合、JSX Pragmaを忘れずにつける必要がありました。気を付けたいですね
- 投稿日:2020-07-14T12:52:27+09:00
styled-componentsを使ってCSS Animationを実装する方法
はじめに
styled-components
はReactコンポーネント内で使えるCSS in JSの一種です。
スコープをコンポーネントのローカルに保てる点や、Sass記法で記述できる点などが特徴です。通常のCSSであれば
@keyframes
を使ってアニメーションを記述できますが、styled-componentsではどうでしょうか。styled-componentsでのkeyframes
結論から言うと、ほぼpure CSSと同様の記述でkeyframesを実装できます。
(keyframesのimport文が必要になります)animation.jsimport styled, { keyframes } from 'styled-components'; const fadeIn = keyframes` from { opacity: 0; } to { opacity: 1; } `; const FadeIn = styled.div` animation: ${fadeIn} .5s ease-in-out; `; // JSXで<FadeIn>コンポーネントを使うとkeyframes: fadeInが適用される参考
- 投稿日:2020-07-14T12:49:41+09:00
propsの引き渡し
- 投稿日:2020-07-14T11:21:02+09:00
[Chakra UI]TypeError: Cannot use 'in' operator to search for 'green' in undefined
- 投稿日:2020-07-14T10:57:48+09:00
Next.jsで"document is not defined." "window is not defined."のエラーが出たとき
Next.jsで"document is not defined." "window is not defined."のエラーが出たとき
原因
Next.jsのSSRが原因である可能性が高いです。
SSR = Server Side Rendering
本来クライアント(ブラウザ)側でしか動かないJSフレームワークなどによるレンダリングをサーバー側で行うことによって、初期表示時の高速読み込みや検索エンジン最適化(SEO)を向上するもの。
サーバー側でJSの処理を実行しようとしているときに、ブラウザ側にしか存在しないグローバルオブジェクトの
window
やdocument
を参照しようとすると、標題のエラーが起こります。対策
1.
componentDidMount()
内で処理を行う
componentDidMount()
内に書いた処理は、基本的にcomponentがmountされた後にクライアント側のみで起こる処理なので、このエラーは起こらないようです。2. if文で条件分岐
React Hooksが主流の現在、こちらの方が汎用性が高いです。
ssr1.jsif (process.browser) { // windowやdocumentを使う処理を記述 }もしくはシンプルに:
ssr2.jsif (window) { // windowを使う処理を記述 }なお
undefined
を判定基準として使うことは一般的にアンチパターンとされるため、下記は避けたほうがいいと思われます。ssr3.jsif (typeof window !== "undefined") { // windowを使う処理を記述 }参考
- 投稿日:2020-07-14T10:57:48+09:00
Next.jsで"document is not defined." "window is not defined."のエラーが出たときの対処法
Next.jsで"document is not defined." "window is not defined."のエラーが出たときの対処法
原因
Next.jsのSSRが原因である可能性が高いです。
SSR = Server Side Rendering
本来クライアント(ブラウザ)側でしか動かないJSフレームワークなどによるレンダリングをサーバー側で行うことによって、初期表示時の高速読み込みや検索エンジン最適化(SEO)を向上するもの。
サーバー側でJSの処理を実行しようとしているときに、ブラウザ側にしか存在しないグローバルオブジェクトの
window
やdocument
を参照しようとすると、標題のエラーが起こります。対策
1.
componentDidMount()
内で処理を行う
componentDidMount()
内に書いた処理は、基本的にcomponentがmountされた後にクライアント側のみで起こる処理なので、このエラーは起こらないようです。ただ、
componentDidMount()
はクラスコンポーネントのライフサイクルメソッドなので、関数コンポーネントでは使えません。2. if文で条件分岐
React Hooksが主流の現在、こちらの方が汎用性が高いです。
ssr1.jsif (process.browser) { // windowやdocumentを使う処理を記述 }もしくはシンプルに:
ssr2.jsif (window) { // windowを使う処理を記述 }なお
undefined
を判定基準として使うことは一般的にアンチパターンとされるため、下記は避けたほうがいいと思われます。ssr3.jsif (typeof window !== "undefined") { // windowを使う処理を記述 }参考
- 投稿日:2020-07-14T09:59:29+09:00
asyncとawaitの関係性、Promiseとは?(typescript, javascript)
対象
async, awaitがさっぱりわからない方
aysncとは
async「非同期処理」↔︎ sync「同期処理」
そもそも同期処理って?
同期処理とは日々我々が書いている普通のコードです。
同期処理 = コードの上から順に読まれること
重たい処理だと表示に時間がかかるため、ユーザーがイライラしてしまう。じゃあ非同期処理(async)は?
非同期処理 = 重い(時間のかかる)処理の結果を待たずに次の行のコードを読み始める処理
AwaitとPromiseとの関係性
await → Promiseの値が取り出されるまで待つ。
async → awaitキーワードを使っている関数のあたまに付ける必要がある。//Aは非同期処理 function A() { return Promise.resolve(1) } async function main() { console.log(1 + A()) // 1が出力 console.log(1 + await A()) // 2が出力 } main()まだPromiseについてはっきりしないので理解出来次第また更新します。
基礎イメージが掴めたらこちらへ(さらに詳しく)
- 投稿日:2020-07-14T08:22:29+09:00
React + TypeScript 環境構築 + ビルド環境を Flavor で切り替える (not create-react-app)
はじめに
この記事は
React
+TypeScript
な環境を作成し、Flavor
を設定してビルド環境(Development
,Staging
,Production
)を切り替えられるようなProject
を作成することを目的としています。React + TypeScript 環境の構築
初期設定
サックっと初期設定してしまいましょう。
$ mkdir react-flavor-example $ cd react-flavor-example $ npm init色々聞かれるので、適当に質問に答えましょう。
package name: (app) react-flavor-example version: (1.0.0) description: entry point: (main.tsx) webpack.config.js test command: git repository: keywords: author: yukitaka13-1110 license: (ISC) About to write to /app/package.json:Dependency の追加
React 系
$ npm i -D react react-dom @types/react @types/react-domWebpack 系
$ npm i -D webpack webpack-cli webpack-dev-server html-webpack-pluginTypeScript 系
$ npm i -D typescript ts-loader
tsconfig.json
の作成tsconfig.json{ "compilerOptions": { "sourceMap": true, "module": "commonjs", "esModuleInterop": true, "resolveJsonModule": true, "experimentalDecorators": true, "target": "es5", "jsx": "react", "lib": [ "dom", "es6" ] }, "include": [ "src" ], "compileOnSave": true }
webpack.config.js
の作成webpack.config.jsconst HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { mode: 'development', entry: './src/main.tsx', plugins: [ new HtmlWebpackPlugin({ template: 'public/index.html', }), ], module: { rules: [ { test: /\.tsx?$/, use: 'ts-loader', }, ], }, resolve: { extensions: [ '.ts', '.js', 'tsx', ], }, };
index.html
の作成
public/
以下にindex.html
を作成します。public/index.html<html lang="ja"> <body> <div id="root" /> </body> </html>
main.tsx
の作成
src/
以下にmain.tsx
を作成します。src/main.tsximport * as React from "react"; import * as ReactDOM from "react-dom"; const App: React.FC = () => { return <div>React Flavor Example</div> }; ReactDOM.render(<App />, document.getElementById("root"));この時点でディレクトリ構造が以下のようになっていればOKです。
$ tree ./ -L 2 ./ ├── node_modules ├── package-lock.json ├── package.json ├── public │ └── index.html ├── src │ └── main.tsx ├── tsconfig.json └── webpack.config.js
package.json
のscripts
にコマンドを追加します。package.json{ ... "scripts": { "start": "webpack-dev-server", "test": "echo \"Error: no test specified\" && exit 1" }, ... }
npm run start
で起動してhttp://localhost:8080/
へアクセスしてReact Flavor Example
という文字が表示されることを確認Flavor の設定
ここからが本題です。
アプリで利用する環境変数を切り替えたいということは良くあることだと思います。
node.js
ではprocess.env.NODE_ENV
という環境変数を利用することでアプリで利用する環境変数を切り替えることができるようです。
しかし、process.env.NODE_ENV
がstaging
を受け取ることができません。型定義を見る感じは受け取れそうですが、どうやらdevelopment
|production
|test
以外は受け付けないようにないっているみたい...。(良く分かっていない)@types/nodeinterface ProcessEnv { [key: string]: string | undefined; }そもそも型定義も貧弱なのでそこも含めて解決します。
process.env
の型を上書きする
process.env
の型を上書きするには、プロジェクト内のどこかにglobal.d.ts
ファイルを作成し、以下のように書くことで可能です。src/types/global/global.d.ts/// <reference types="node" /> declare namespace NodeJS { interface ProcessEnv { readonly NODE_ENV: 'development' | 'production' | 'test'; readonly FLAVOR: 'development' | 'staging' | 'production'; } }
NODE_ENV
は触っちゃいけない気がしたので以下のようにFLAVOR
を生やしてみました。その後、
package.json
でFLAVOR
を渡すscripts
を追加します。package.json{ "scripts": { "start": "webpack-dev-server", + "dev": "FLAVOR=\"development\" webpack-dev-server", + "stg": "FLAVOR=\"staging\" webpack-dev-server", + "prod": "FLAVOR=\"production\" webpack-dev-server", "test": "echo \"Error: no test specified\" && exit 1" }, }渡しただけではアプリ側では受け取れないので、
webpack.config.js
で工夫します。webpack.config.jsconst HtmlWebpackPlugin = require('html-webpack-plugin'); + const webpack = require('webpack'); + const flavor = process.env.FLAVOR || 'development'; module.exports = { mode: 'development', entry: './src/main.tsx', plugins: [ new HtmlWebpackPlugin({ template: 'public/index.html', }), + new webpack.DefinePlugin({ + 'process.env.FLAVOR': JSON.stringify(flavor), + }) ], }
main.tsx
にconsole.log()
を追加してみましょう。
先程、型定義を上書きしたので補完が効くようになっています。試しに
npm run stg
を実行してみましょう。
ちゃんと値を受け取れています。後は適当に環境毎に変数を定義して
process.env.FLAVOR
を使って分岐するだけです。
例えば以下のようなconfig
ディレクトリを用意しておけば良いでしょう。./src/config ├── development │ └── index.ts ├── index.ts ├── production │ └── index.ts └── staging └── index.tsstaging/index.tsimport { EnvVariables } from '../'; const stgVariables: EnvVariables = { HOGE: 'STAGING_HOGE', FUGA: 'STAGING_FUGA', }; export default stgVariables;index.tsimport devVariables from './development'; import stgVariables from './staging'; import prodVariables from './production'; export type EnvVariables = { HOGE: string; FUGA: string; }; const envVariables = (): EnvVariables => { if (process.env.FLAVOR === 'production') { return prodVariables; } if (process.env.FLAVOR === 'staging') { return stgVariables; } return devVariables; }; export default envVariables();src/main.tsximport * as React from "react"; import * as ReactDOM from "react-dom"; import envVariables from './config' // Add import; const App: React.FC = () => { return ( <div> <div>React Flavor Example</div> <div>{envVariables.HOGE}</div> <div>{envVariables.FUGA}</div> </div> ); }; ReactDOM.render(<App />, document.getElementById("root"));
npm run stg
を実行してhttp://localhost:8080/
にアクセスします。ちゃんと値を表示することができました。
終わりに
今回使用したコードはこちらに載せています。
https://github.com/yukitaka13-1110/react-flavor-example現在、QUANDO では iOS, Android, Flutter エンジニア募集中です。
- 投稿日:2020-07-14T03:15:56+09:00
react Hooks useState 使ってみた
useStateを実際に使おう
useStateとは?
Returns a stateful value, and a function to update it.
During the initial render, the returned state (state) is the same as the value passed as the first argument (initialState).
The setState function is used to update the state. It accepts a new state value and enqueues a re-render of the component.
(react 公式サイトuseStateより引用)
React公式サイトにはこのように書いています。
ん?わからない。
なので実際に簡単なコードを書いてみました。
※これはReact Application上で書きました
コード1
const example = useState(0) console.log({example})この場合のconsole.logの出力は次のようになります。
example: Array(2)これの中身を見るとこのようになっています。
0: 0 1: f()つまりexampleは配列になっています。
1番目には初期値である0が
2番目には関数が入ります。コード2
useState()の括弧の中が
本当に初期値なのかと疑問に思ったので
console.logしてみました。const example = useState(500) console.log({example})この場合のexampleの配列の中身は次のようになります。
0: 500 1: f()ちゃんと初期値になってます!
コード3
この配列には2つの要素が入ることがわかりました。
ここでjavascriptの分割代入を使って
それぞれの要素を受け取るようにします。const [count, setCount] = useState(0); const up =() => setCount(count +1)ここでupはcountを+1します。
countの状態を変えるために
setCountをこのように使います。
これによりcountの値を+1していくことができます。実際に書いたコード
import react, {useState} from 'react'; const App = () => { const [count, setCount] = useState(0); const up =() => setCount(count +1) const down =() => setCount(count -1) return ( <> <div>count: {count}</div> <div> <button onClick={up}>+1</button> <button onClick={down}>-1</button> </div> </> ); } export default App;学習したことを応用して
簡単なカウンターを作ってみました。buttonタグでボタンを表示し
onClickはボタンを押したときの処理です。
こうすることでボタンを押して
+1,-1できるカウンターの出来上がりです!
- 投稿日:2020-07-14T00:15:57+09:00
【メモ】react-native にて FontAwesome を急に読み込めなくなった問題
react-native にて FontAwesome を急に読み込めなくなった問題
業務で
react-native
を扱うことになり、 udemy で講座を購入し、勉強していたのですが急にFontAwesomeが読み込めなくなりました。
とりあえず解決できたので、メモがてら記録に残しておきます。環境
- MacOS: Catalina 10.15.3
- react: 16.9.0
- expo-font: 8.1.1
- expo: 37.0.0
エラーメッセージ
console.error: "fontFamily "FontAwesome" is not a system font and has not been loaded through Font.loadAsync. - If you intended to use a system font, make sure you typed the name correctly and that it is supported by your device operating system. - If this is a custom font, be sure to load it with Font.loadAsync."カスタムフォントを使うなら、
Font.loadAsync
を用いてそのフォントを読み込んでね!ってことでしょうか。ソースコード
button.jsimport React from 'react'; import { StyleSheet, View, TouchableHighlight } from 'react-native'; import * as Font from 'expo-font'; import { createIconSet } from '@expo/vector-icons'; import fontAwsome from '../../assets/fonts/fa-solid-900.ttf'; const CustomIcon = createIconSet({ pencil: '\uf303', plus: '\uf067', check: '\uf00c', }, 'FontAwesome'); class CircleButton extends React.Component { state = { fontLoaded: false, }; async componentDidMount() { await Font.loadAsync({ FontAwesome: fontAwsome, }); this.setState({ fontLoaded: true }); } //以下中略 ); } }こんな感じで読み込んでいたのですが、うまいこと読み込んでくれませんでした。
解決
expo をアップデートすることで解決しました。
npm i -g expo-cli expo upgrade参考