Udemy & 本 de お勉強
Promise
resolve
:解決したよの意味。成功したときに呼ばれる。
reject
:拒絶したよの意味。失敗したときに呼ばれる。
- Promise
約束事を決める。
約束守れたらresolve
。守れなかったらreject
- Promise.all
引数に配列を持つ。
指定した引数すべてのPromise
を実行する。
たとえば、Promise.all
の中で30回のfetchをするコードを書いたとする。
30回分全部のfetch
が完了するか、1つでもreject
になった時点で終了する。
コンソールでみると・・
- pending
保留状態
であることを示している。
pending
成功時
CSSボックスシャドウジェネレーター
好みのボックスに影をつけるコードをコピーできる。
(CSSを直接操作するからMUIとか使うならあんま使わないかも?)
CSSボックスシャドウジェネレーター
エラー
Warning: Each child in a list should have a unique "key" prop.
各項目の配列にユニークなkey
を設定してね。の意味。
map関数を使ってJSX要素を記述する時には必要になる。
JSX要素が変更、削除された場合でもReactがkey
を使って要素を追えるようにしておきたい。
参考:keeping-list-items-in-order-with-key
.gitignore
.gitignore
に記載されたファイル、フォルダはgit add
実行時にインデックスに追加されなくなる。
#
はコメント。
行の先頭に/
があれば、.gitignore
が配置されたディレクトリが基準となって除外設定
ディレクトリ配下すべてを除外設定したい場合は[ディレクトリ名]/
とする。
*
および?
のワイルドカードや[ ]
を使用した正規表現も可能。
# どのディレクトリに関わらず、sample.txt を除外
sample.memo
# .gitignoreが配置されたディレクトリ内の sample.txt を除外
/sampe.txt
# sample-1 が見つかるパスを除外
sample-1
# sampleDirectory配下のすべてのパスを除外する。
sampleDirectory/
# sample.js や sample.json など sa から始まるファイルを除外設定
sa*
# すべてのファイルを除外設定
*
タグの命名時の注意
googleSEO
などに反映されやすくなるために、明確なタグをつけておくとよい。
CSS
align-items:center
は交差軸を中心にそろえる。
--□□□□---
みたいな□の要素の中心にそろえられる。
align-items:flex-start
は上にそろえる。交差軸ではなくてっぺんから。
align-items:flex-end
は下にそろえる。
flex-direction:column
は横軸を中央にそろえる。
下記のように書くと横軸の中央線と縦軸の交差点でそろえることができる。
flex-direction: column;
align-items: flex;
ボタンをいい感じにする。
.sampleButton {
padding: 10px 0;
background-color: #3d77a5;
border: none;
box-shadow: 6px 8px 18px -3px #777777;
color: white;
border-radius: 5px;
cursor: pointer;
transition: all 0.3s;
}
box-shadow
は影をつける。今っぽいボタンになるぞ。
cursor
=カーソル。pointer
でマウスカーソルが当たると指の形になるアレ。
transition
に秒数を指定すると、指定時間かけてホバーする。
マウスカーソルがホバーされたときはに沈みこみを発生させる。
.sampleButton:hover{
transform: translateY(5px);
box-shadow: none;
}
transform
でY軸にマイナスを指定する。そうするとY軸に対して指定した分だけ沈み込みする。
bos-shadow:none
にしたのは視覚的にも沈んでいるようにするため。
marign
オレンジの部分がmargin
となる。
margin: 0px
とすると消せるよ。
firebase
signInWithPopup(auth, provider).then((result) => {});
もはやこれだけでログインできるみたい。(このままだとリロードしたらログイン情報消えるけど。)
Cloud FireStore
Collection
=フォルダ。
document
=1枚1枚のページ。
data
=document
に書かれている文書。 だと思うといいかも。
ログインしているユーザーの名前を取得したい時は
import { getAuth, GoogleAuthProvider } from "firebase/auth";
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
(...のこりは略)
const userName = auth.currentUser.displayName;
FirebaseError
Missing or insufficient permissions
insufficient
:不十分な。insufficient permissions
=不十分な許可ですよ。の意味。
firebase
のルールをみるとwrite:false
となっている。
ここをtrue
にすれば今回は解決。
React-router-dom
<Router></Router>
で囲まれた要素にpath
を追加してあげるとルーティングできる。
自由にURLの指定もOK。
import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom";
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<Home />}></Route>
<Route path="/page1" element={<Page1/>}></Route>
<Route path="/page1" element={<Page2 />}></Route>
<Route path="/page1" element={<Page2 />}></Route>
</Routes>
</Router>
);
}
export default App;
{ }
有無
import時の
{ }
があると名前付きインポート
となる。
1つのモジュール内に1つだけエクスポートできるexport default [sample]
をインポートしたい時は、{ }
なしのデフォルトインポートを使用する。
1つのモジュール内に複数のエクスポートをもつファイルからインポートしたい場合は、{ }
付きの名前付きインポートを使用する。
今回のaddDoc
は名前付きインポートで使用する。理由はfirebase
側でaddDoc
の名指しでexportされているから。
ちなみに・・・
他の人のコードをみるといろんなところでindex.js
,index.ts
をみる。
これはエントリーポイント
と呼ばれ、一括でインポートできるようになる。
(コンポーネントが大量にあるとimport
だらけになってとてもつらい思いをした・・・)
Component_1.js;
Component_2.js;
...
Component_300.js;
各コンポーネントをインポートしようとするとファイル毎に行頭から↓のようにimport
だけで300行も書く羽目に・・・
(実際に300行かいた。びっくりするくらい修正めんどくさかったし読みにくかった。)
import Compornent_1 from "/component_1"
import Compornent_2 from "/component_2"
...
import Compornent_300 from "/component_300"
そこでエントリーポイントのindex.js
またはindex.ts
にまとめてしまう。
export Compornent_1 from "/component_1";
...
export Compornent_300 from "/component_300";
これで実際に使用したいファイルでimport * from "./index.js";
としてしまえば、300個のコンポーネントを自由に使えちゃう!
一度300行のインポートをしてしまえばあとはindex.js
をインポートするだけ。いちいちファイル毎に書く必要ななくなる。楽だね。もっと早く知りたかった。
階層の深い構造を処理するとき
data()
関数を指定すると、楽に中身を持ってこれるよ。
スプレッド構文(...list)
に,
区切りでプロパティを指定すると属性を追加することができる。
sample:{
id,
name
}
const test = ({...sampe.data(), telNo:sample.telNo})
とすると・・・
sample:{
id,
name,
telNo
}
日付を表示
Date.now()
でUTC1970年1月1日0時0分0秒から現在までの経過時間をミリ秒単位で取得できる。
const nowDate = Date.now();
console.log(nowDate);
>>> 1689247600796
人間でもわかるようにするにはインスタンス化する。
const nowDate = Date.now();
console.log(new Date(nowDate));
>>> Thu Jul 13 2023 20:33:44 GMT+0900 (日本標準時)
よく見るyyyy/mm/dd
にしたい(しかも日本時間で)!ってときはtoLocalDateString()
メソッドを使う。
const nowDate = Date.now();
console.log(new Date(nowDate).toLocaleDateString("ja-JP"));
>>> 2023/07/13
ほんでさらに時刻を取得したい!ってときはtoLocalDateString()
メソッドの第二引数(option)を設定してあげればいい。(2-digit
は二桁
の意味)
const nowDate = Date.now();
console.log(new Date(nowDate).toLocaleDateString("ja-JP",{
hour:"2-digit",minute:"2-digit"
}));
>>> 2023/7/13 20:43
めっちゃ楽じゃん。これは苦労した覚えがある。
クリックイベント等を呼び足すときの違い
以下の書き方だとページをリロードしたときにonClickHandler()
が発火される。
デフォルトで絶対発火されることになる。
<button onClick={onClickHandler()}>ボタン1</button>
onClickHandler()
に対して引数を取りたい時や、クリックされた時だけ発火するようにしたい場合は以下のようにアロー関数で記述する。
<button onClick={() => onClickHandler()}>ボタン2</button>
filter
とある配列から指定した条件がTrue
のデータのみを抽出して新たな配列を生成する。
const saibaiLists=[
{category:"果物",name:"みかん"},
{category:"野菜",name:"すいか"},
{category:"果物",name:"ぶどう"},
{category:"野菜",name:"いちご"},
]
こんな配列があったとする。この配列からcategory="果物"
のデータを抽出したい。
const kudamonoLists = saibaiLists.filter((saibaiList) => saibaiList.category === "果物");
kudamonoLists
は以下のようになる。
動的キー
[ ]
囲みでkey
を指定すると、自分で設定した変数をkey
として与えることができる。
const sampleKey1 = "ThisIsKey"
const sampleObj1 = {
[sampleKey1]:"ThisIsValue"
}
const sampleKey2 = "ThisIsKeySecond!!"
const sampleObj2 = {
[sampleKey2]:"ThisIsValue"
}
再レンダリング
どんな時に再レンダリングするの?
-
state
が更新されたコンポーネントは再レンダリング -
props
が変更されたコンポーネントは再レンダリング - 再レンダリングされたコンポーネント配下の子要素は再レンダリング
この時、B
でstate
が更新されたら、レンダリングされるのはB
とC
になる。
今の状態
import { useState } from "react";
import "./App.css";
import { ChildArea } from "./ChildArea";
function App() {
console.log("app");
const [text, setText] = useState("");
const [open, setOpen] = useState(false);
const onChangeText = (e) => setText(e.target.value);
const onClickOpen = () => {
setOpen(!open);
};
return (
<div>
<input value={text} onChange={onChangeText} />
<br />
<br />
<button onClick={onClickOpen}>表示</button>
<ChildArea open={open} />
</div>
);
}
export default App;
const style = {
width: "100%",
height: "200px",
backgroundColor: "khaki",
};
export const ChildArea = (props) => {
const { open } = props;
console.log("ChildAreaがレンダリングされた");
const data = [...Array(2000).keys()];
data.forEach(() => {
console.log("...");
});
return (
<>
{open ? (
<div style={style}>
<p>子コンポーネント</p>
</div>
) : null}
</>
);
};
どう対策するの?
1 memo化
Propsの値をチェックして再レンダリングを判定。
同じpropsで再レンダリングが行われるときは、前回の再レンダリング結果を再利用するため、子コンポーネントを再レンダリングしない。
複数の要素から成り立つコンポーネントや肥大化が予想されるコンポーネントはmemo
化するといい。
コード
import { memo } from "react";
const style = {
width: "100%",
height: "200px",
backgroundColor: "khaki",
};
export const ChildArea = memo((props) => {
const { open } = props;
console.log("ChildAreaがレンダリングされた");
const data = [...Array(2000).keys()];
data.forEach(() => {
console.log("...");
});
return (
<>
{open ? (
<div style={style}>
<p>子コンポーネント</p>
</div>
) : null}
</>
);
});
しかし、これでは以下のようにprops
に関数を渡すと子要素も再レンダリングされてしまう。
const [open, setOpen] = useState(false);
const onClickClose = () => setOpen(false);
return (
<div>
<input value={text} onChange={onChangeText} />
<br />
<br />
<button onClick={onClickOpen}>表示</button>
+ <ChildArea open={open} onClickClose={onClickClose} />
</div>
);
return (
<>
{open ? (
<div style={style}>
<p>子コンポーネント</p>
+ <button onClick={onClickClose}>閉じる</button>
</div>
) : null}
</>
);
2 useCallback(関数のmemo化)
propsに渡す関数が前回実行された関数と変更なければ前回のレンダリングを再利用する。
useEffect
同様に第二引数に監視する値をセットする。
(第二引数に変更があったときにCallbackする)
- const onClickClose = () => setOpen(false), [setOpen];
+ const onClickClose = useCallback(() => setOpen(false), [setOpen]);
3 useMemo(変数のメモ化)
変数をメモ化する。
useEffect同様に第二引数に監視する値をセットする。から配列を指定すると最初に読み込まれた一回だけ実行される。
変数に設定する処理が複雑になるときに使用すると、再レンダリングの回数が減るのでgood。
+ const temp = useMemo(() => 1 + 3, []);
+ console.log(temp);
404ページの作成(React-Router v5)
404ページ自体はメッセージとホームへのリンクのみ。
import { Link } from "react-router-dom";
export const Page404 = () => {
return (
<div>
<h1>ページが見つかりません。</h1>
<Link to="/">TOPに戻る</Link>
</div>
);
};
どのルートにも一致しなかった場合に404ページに遷移させる。Routerコンポーネントの最後を追記。
path="*"
はべて一致したら
の意味。
URLが変更されたら<Switch>
タグのpath
を順番にレンダリングしていき、最後まで来たとき(<Route path="*">
)はどれにも一致していないので<Page404 />
をレンダリングする。
+ <Route path="*">
+ <Page404 />
+ </Route>
Router.jsx全体
import { Switch, Route } from "react-router-dom";
import { Home } from "../Home";
import { Page404 } from "../Page404";
import { Page1Routes } from "./Page1Routes";
import { Page2Routes } from "./Page2Routes";
export const Router = () => {
return (
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route
path="/page1"
render={({ match: { url } }) => (
<Switch>
{Page1Routes.map((route) => (
<Route
key={route.path}
exact={route.exact}
path={`${url}${route.path}`}
>
{route.children}
</Route>
))}
</Switch>
)}
/>
<Route
path="/page2"
render={({ match: { url } }) => (
<Switch>
{Page2Routes.map((route) => (
<Route
key={route.path}
exact={route.exact}
path={`${url}${route.path}`}
>
{route.children}
</Route>
))}
</Switch>
)}
/>
<Route path="*">
<Page404 />
</Route>
</Switch>
);
};
共通のデザインを適用する(styled-component)
デザインだけをもったコンポーネントをエクスポートすることによって何度も書かずともデザインを適用できる。
import styled from "styled-components";
export const BaseButton = styled.button`
color: #fff;
padding: 6px 24px;
border: none;
border-radius: 9999px;
outline: none;
&:hover {
cursor: pointer;
opacity: 0.8;
}
`;
BaseButton
のデザインを適用したいコンポーネントでインポートする。
さらにデザインを追加したい場合はインポートしたBaseButton
にデザインを追加してあげるイメージ。
import React from "react";
import styled from "styled-components";
+ import { BaseButton } from "./BaseButton";
export const PrimaryButton = (props) => {
const { children } = props;
return <SButton>{children}</SButton>;
};
+ const SButton = styled(BaseButton)`
+ background-color: #40514e;
+ }
`;
import React from "react";
import styled from "styled-components";
+ import { BaseButton } from "./BaseButton";
export const SecondaryButton = (props) => {
const { children } = props;
return <SButton>{children}</SButton>;
};
+ const SButton = styled(BaseButton)`
+ background-color: #11999e;
+ }
`;
グローバルなstate管理
1 useContext
createContext
はReactで用意されているフック。Provider
を使って、ほかのコンポーネントにデータを提供する。
Provider
で囲ったコンポーネントでは、Contextのvalue
を呼び出せる。
import React, { createContext } from "react";
export const UserContext = createContext({});
export const UserProvider = (props) => {
const { children } = props;
const contextName = "名前だよ!!!!";
return (
<UserContext.Provider value={{ contextName }}>
{children}
</UserContext.Provider>
);
};
function App() {
return (
<UserProvider>
<Router2 />
</UserProvider>
);
}
今回の例では、<Router2>
はUserProvider
の contextName(名前だよ!!!!)
を呼び出すことができる。
実際に使うときは、useContext
をインポートする。
どのコンテキストをつかうか?というのはUserProvider.jsx
でエクスポートしているUserContext
を指定する。
import React, { useContext } from "react";
const context = useContext(UserContext);
console.log(context)
→ 名前だよ!!!!
実際には変数を使うことが多い。その場合はvalue
に使いたい変数を渡す。
以下のように書いてあげることによって、UserContext.Provider
配下でstate‘も
setState‘を呼び出すことができる。
export const UserProvider = (props) => {
const { children } = props;
+ const [userInfo, setUserInfo] = useState(null);
- const contextName = "名前だよ!!!!";
return (
+ <UserContext.Provider value={{ userInfo, setUserInfo }}>
- <UserContext.Provider value={{ contextName }}>
{children}
</UserContext.Provider>
);
};
Recoil
まだ正式公開されていないが、Metaが開発しているグローバルなstate管理ができる状態管理ライブラリ。
jsxを返却するわけではないから、.js
ファイルでok。
npm install recoil
でインストール。
key
にはユニークな値を設定。(ファイル名とそろえるといいかも)
default
デフォルトで設定する値。(初期値だと思えばいいかな)
import { atom } from "recoil";
export const userState = atom({
key: "userState",
default: { isAdmin: false },
});
グローバルで使えるようにRecoilRoot
タグで囲む。
import { RecoilRoot } from "recoil";
function App() {
return (
+ <RecoilRoot>
<UserProvider>
<Router2 />
</UserProvider>
+ </RecoilRoot>
);
}
export default App;
state
とset関数を参照したい場合はuseRecoilState
をインポートする。
+ import { useRecoilState } from "recoil";
export const Users = () => {
+ const [userInfo, setUserInfo] = useRecoilState(userState);
- const { userInfo, setUserInfo } = useContext(UserContext);
const onClickSwith = () => {
setUserInfo({ isAdmin: !userInfo.isAdmin });
};
return (
<SContainer>
<h2>ユーザー一覧</h2>
<SearchInput />
<br />
<SecondaryButton onClick={onClickSwith}>切り替え</SecondaryButton>
<SUserArea>
{users.map((user) => (
<UserCard key={user.id} user={user} />
))}
</SUserArea>
</SContainer>
);
};
値のみを参照したい場合はuseRecoilValue
をインポートする。
+ import { useRecoilValue } from "recoil";
+ const userInfo = useRecoilValue(userState)
- const { userInfo } = useContext(UserContext);
set関数のみを参照したい場合はuseSetRecoilState
をインポートする。
値は参照しないけどstateだけ更新したい!ってときに使う。
更新するset関数を受け取っているコンポーネントはstateの値が更新されても再レンダリングはされない。
+ import { useSetRecoilState } from "recoil";
+ const setUserInfo = useSetRecoilState(userState);
- const { setUserInfo } = useContext(UserContext);
Pick<>
ある型から特定の要素を取得して使用したい場合はPick
を使う。
Pick<"元の型","欲しい型1" | 欲しい型2">
export type TodoType = {
userId: number;
id: number;
title: string;
completed: boolean;
};
userId
,title
,completed`だけを取得したいときは・・・
export const Todo = (
+ props: Pick<TodoType, "userId" | "title" | "completed">
) => {
const { title, userId, completed = false } = props;
const completeMark = completed ? "[完]" : "[未]";
return <p>{`${completeMark} ${title}(ユーザー:${userId})`}</p>;
};
Omit<>
Pick
の逆で、特定の要素を除いたものを使用したい場合に使う。
id
以外の要素を取得したいときは・・・
+ export const Todo = (props: Omit<TodoType, "id">) => {
const { title, userId, completed = false } = props;
const completeMark = completed ? "[完]" : "[未]";
return <p>{`${completeMark} ${title}(ユーザー:${userId})`}</p>;
};
関数の型定義
関数名にFC
,VFC
を追加して関数を記述する。
※FC
は暗黙的にchildren
が含まれていたが、React V18
以降は明示的に指定しないとエラーになるため注意
+ import { FC } from "react";
type Props = {
color: string;
fontSize: string;
};
+ export const Text: FC<Props> = (props) => {
const { color, fontSize } = props;
return <p style={{ color, fontSize }}>テキストです</p>;
};
FC
からchildren
を除くためにVFC
が用意されていたが、React V18
以降は明示的に指定しないといけなくなったため非推奨となった。
import { VFC } from "react";
type Props = {
color: string;
fontSize: string;
};
export const Text: VFC<Props> = (props) => {
const { color, fontSize } = props;
return <p style={{ color, fontSize }}>テキストです</p>;
};
オプショナルチェーン
存在しないプロパティにアクセスしてもエラーにならずundefind
を返す。
以下のようなコードがあったとする。user
にname
とhobbies
がそれぞれ存在していればエラーなく表示される。
export const UserProfile: VFC<Props> = (props) => {
const { user } = props;
return (
<dl>
<dt>名前</dt>
<dd>{user.name}</dd>
<dt>趣味</dt>
<dd>{user.hobbies.join(" / ")}</dd>
</dl>
);
};
const user: User = {
name: "namae",
hobbies: ["映画", "散歩"],
};
hobbies
が存在しないとエラー。
const user: User = {
name: "namae",
- hobbies: ["映画", "散歩"],
};
そこで、オプショナルチェーンを使うと上述の通り、存在しないプロパティはundefind
となる。
export const UserProfile: VFC<Props> = (props) => {
const { user } = props;
return (
<dl>
<dt>名前</dt>
<dd>{user.name}</dd>
<dt>趣味</dt>
+ <dd>{user.hobbies?.join(" / ")}</dd>
- <dd>{user.hobbies.join(" / ")}</dd>
</dl>
);
};
エラーもなしヨシ!
カスタムフックって?
- 関数。
-
useState
,useEffect
だったりのReactの各関数を利用できる。 - コンポーネントからロジックを分離。
- ロジックから分離することによって、使いまわし、テストが楽になる。
-
use○○
という名前にする。
例(ユーザー一覧を取得)
onClick
に書いている内容をそのまま関数化するイメージ。
他のファイルでも使用できるようにするためには、配列またはオブジェクトでreturnする。
個人的にはオブジェクトのが好み。
import { useState } from "react";
import axios from "../../node_modules/axios/index";
import { UserApi } from "../types/api/userApi";
import { UserProfile } from "../types/userProfile";
//全ユーザー一覧を取得するカスタムフック
export const useAllUsers = () => {
const [userProfiles, setUserProfiles] = useState<Array<UserProfile>>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(false);
const getUsers = () => {
setLoading(true);
setError(false);
axios
.get<Array<UserApi>>("https://jsonplaceholder.typicode.com/users")
.then((res) => {
const data = res.data.map((user) => ({
id: user.id,
name: `${user.name}(${user.username})`,
}));
setUserProfiles(data);
})
.catch(() => {
setError(true);
})
.finally(() => {
setLoading(false);
});
};
return {
getUsers,
userProfiles,
loading,
error,
};
};
作成したカスタムフックを使うにはこのように書く。
import { useAllUsers } from "./hooks/useAllUsers";
function App() {
const { getUsers, userProfiles, loading, error } = useAllUsers();
const onClickHandler = () => {
axios
.get<Array<TodoType>>("https://jsonplaceholder.typicode.com/todos")
.then((res) => {
setTodos(res.data);
});
};
return (
<>
<button onClick={onClickFetchUser}>データ取得</button>
<br />
{error ? (
<p style={{ color: "red" }}>データ取得に失敗しました。</p>
) : loading ? (
<p>Loading....</p>
) : (
<>
{userProfiles.map((user) => (
<UserCard key={user.id} user={user} />
))}
</>
)}
</>
);
}
呼び出したconst { getUsers, userProfiles, loading, error } = useAllUsers();
はそれぞれのコンポーネントで独立となる。
他のコンポーネントでuseAllUsers
を呼び出したとしても競合することはない。
プロパティ名の動的生成
プロパティ名をブラケットで括ることによって、式の値からプロパティ名を生成できる。(算出プロパティ名 )
i
の値によって、element1
、element2
・・とプロパティが生成される。
let i = 0;
const array = {
[`element${++i}`]: '○○ ○○',
[`element${++i}`]: '性別',
[`element${++i}`]: '年齢',
};
console.log(array);
他の例
[e.target.value]:e.target.value
要素の名前(e.target.name)をそのままプロパティ名として、入力値(e.target.value)を渡しなさい 意味。
分割代入
const originalList = [10,20,30];
const [x, y, z] = originalList;
console.log(x,y,z);
→(結果) 10 20 30
左辺に要素の数だけ変数を列挙、し、全体を[ ]
で括る。
実際の配列サイズより代入先が少なければ残りの要素は無視される。
const [a,b] = originalList;
console.log(a,b);
→(結果) 10 20
実際の配列サイズより代入先が多ければ、その分はundefined
になる。
const [c,e,f,g] = originalList;
console.log(c,e,f,g)
→(結果) 10 20 30 undefined
一部だけの場合は・・・
const [h, ,j] = originalList;
console.log(h,j);
→(結果) 10 30
オブジェクトがブロックとみなされてしまう例
const teamMember ={
fullName: '○○ 太郎',
gen:'male',
age: 30
};
let fullName, gen, memo;
とする。
{ fullName, gen, memo = 'none' } = teamMember;
→(結果) Uncaught SyntaxError: Unexpected token '='
左辺の{ }
がブロックとしてみなされずに、ただの文として扱われたためにエラーとなる。
正しくは、代入文の前後を( )
で括る。
({ fullName, gen, memo = 'none' } = teamMember);
→(結果) {fullName: '○○ 太郎', gen: 'male', age: 30}
これはアロー関数でも同じこと。
自分はよくやりがちだから注意する。
const function1 = () => { title: 'タイトル' };
→(戻り値) undefined
{ }
のブロック内に、ラベル + 文字列
が存在するだけの関数とみなされている。オブジェクトリテラルとして正しく認識されるようにしたい場合は( )
で括る。
const function1 = () => ({ title:'タイトル' });
→(戻り値) () => ({ title:'タイトル' })
可変長引数の関数
仮引数の前に...
を付与するとこと可変長引数となる。任意の引数を配列として取得する。
function sum(...hikisu){
let result = 0;
for (const val of hikisu){
result += val;
}
return result;
}
console.log(sum(10,11,12));
→(結果) 33
console.log(sum(1,2,3,4,5,6,7,8,9,10));
→(結果) 55
普通の引数と併用したいときは、引数の最後に配置すること。
×: function badFunction(...kahenHikisu, hikisu){};
○: function goodFunction(hikisu,...kahenHikisu){};
(ざっくりとでも) ReactDOM.createRoot って?
createRoot(hikisu1,[option])
メソッドはReactアプリを埋め込むべきルート要素を特定する。
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
これは、
<React.StrictMode>配下の<App>コンポーネントを実行して、その結果をid="root"
に描画してね
の意味になる。
関数コンポーネントとクラスコンポーネント
どちらも同じ動作をするコンポーネント。
import logo from "./logo.svg";
import "./App.css";
// Appコンポーネントを定義
function App() {
// 戻り値
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
こんにちは、 React!
</a>
</header>
</div>
);
}
// Appコンポーネントをエクスポート
export default App;
// import は省略
// AppClassコンポーネントを定義
class AppClass extends React.Component{
// 描画する内容
render(){
return(
<div className="App">
// 関数コンポーネントと同じ内容のため省略
</div>
)
}
}
//AppClassコンポーネントをエクスポート
export default AppClass;
ReactComponent
クラスを継承し、render
メソッドで出力することがクラスコンポーネントを定義する上で必要。
クラスコンポーネントは、んでどっちのがいいの?
シンプルな関数コンポーネントのがいいかも。(というか今までの学習のほとんどが関数コンポーネントだった)
Meta的にも関数コンポーネントがメインだから倣って進めよう。
エスケープ
{ }
で括られた式はエスケープ処理される。
{ }
で&
などの文字を表したい場合は、エスケープシーケンスを使おう。
<div>{"Guri & Gura"}</div>
→(結果) Guri & Gura
<div>Guri & Gura</div>
→(結果) Guri & Gura
// エスケープシーケンス
<div>{'Guri \u0026 Gura'}</div>
→(結果) Guri & Gura
Propsの分割代入
引数としてprops
を受け取るのはよいが、関数内ではprops.○○
と記述する。
Propsの数が増えると面倒になる。分割代入を使うとコードが少しだけ短くなる。また規定値を宣言できるようになる。
export default function MySamplePorps(props) {
return <div>これがmyPropsの値だよ→ {props.myProps}</div>;
}
export default function MySamplePorps({ myProps }) {
return <div>これがmyPropsの値だよ→ {myProps}</div>;
}
<MySamplePorps myName="分割代入を使ってないよ!" />
<MySamplePorps myName="分割代入を使っているよ!" />
結果
表示された結果は同じだよ。
規定値を指定した場合は以下の通り。
export default function MySamplePorps({ myProps = 'これは規定値だよ!' }) {
return <div>これがmyPropsの値だよ→ {myProps}</div>;
}
<MySamplePorps />
結果
map関数におけるkeyについて
配列要素が主キーを持たない場合にはmap
関数のindex
を使うことがある。
{
datas.map((data,index) => (
<React.Fragment key={index}>
<dt>(略)</dt>
<dt>{data.summary}</dt>
</React.Fragment>
)
)
}
一見するとindex
は一意の値に見えるが、要素の追加や削除、並び替えによって変化する可能性がある。
将来的に見て、バグを引き起こすリスクを持つくらいならリスト内の要素は一意のkeyを持つようにすること。
入れ子構造のコンポーネントに対してmap
関数を使う場合は、keyを属性の意味を考えてから設定すること。
たとえば、リスト全体を管理するZentai.js
と個々の内容を管理するItem.js
があったとする。
import Item from "./Item";
export default function Zentai({ src }) {
return (
<dl>
{src.map((elem) => (
<ForItem itemNaiyo={elem} key={elem.cd} />
))}
</dl>
);
}
export default function Item({ book }) {
return (
<React.Fragment>
{book.summary}
</React.Fragment>
);
}
keyを追加する必要があるのは、Item
コンポーネント配下の<>~</>
ではなく、Item
コンポーネントそのものに対して追加する必要がある。
≒ map呼び出しの中に現れる要素に対して追加する。
classnames
className属性へのスタイル指定をする場合に{ }
が混雑になってくる。
そんなんときに便利になってくるのがclassnames
ライブラリ。
動的なクラスの使い分け、複数のクラス名をつけるときに使うとよい。
import classNames from 'classnames;
export default function ClassNameStyles({hikisu}){
return(
<div className={classNames('box', hikisu === 'light' ? 'light' : 'dark')}>
内容だよ!!!
</div>
);
}
<SelectStyle hikisu='light' />
結果をコンソールでみると・・・
このライブラリを使うことによって、className
属性にスタイル指定をするときに、見やすくなる(特にある条件のみにスタイル適用する場合など)
<div className={hikisu === 'light' ? 'light' : 'dark'}>
(省略)
</div>
childrenに対してパラメーターを引き渡すときの注意
props.children
が表すコンテンツは呼び出し元のスコープであるから、コンポーネント配下の情報にはアクセスできない。
次の例は、配列sampleArray
を出力するテンプレートをindex.js
で指定できることを目的としているが、エラーになる。
export default function ListSampleTemplate({src,children}){
return(
<dl>
{src.map(data => (
<React.Fragment key={data.keyValue}>
{children}
</React.Fragment>
}
</dl>
);
}
(省略)
<ListSampleTemplate src={sampleArray}>
<dt>
<p>{data.firstName} {data.lastName}</p>
</dt>
</ListSampleTemplate>
結果
index.js
でdata
参照できない。
なぜならdata
は、ListSampleTemplate
のmap
で定義されている変数だから。
このエラーを解決したい場合は呼び出し元配下のテンプレートを関数化することによって解決できる。
関数化することによって、map
で取り出した配列要素を引き渡すことによって、呼び出し元からそれぞれの情報を参照することができるようになる。
export default function ListSampleTemplate({src,children}){
return(
<dl>
{src.map(data => (
<React.Fragment key={data.keyValue}>
{children(elem)}
</React.Fragment>
}
</dl>
);
}
(省略)
<ListSampleTemplate src={sampleArray}>
{
<>
<dt>
<p>{data.firstName} {data.lastName}</p>
</dt>
</>
}
</ListSampleTemplate>
描画のための関数 レンダープロップ
レンダープロップを利用することによって、データの取得や演算のような共通機能をコンポーネントで定義し描画方法は利用する人が自由に決められるようになる。
+ export default function ListSampleTemplate({src,render}){
- export default function ListSampleTemplate({src,children}){
return(
<dl>
{src.map(data => (
<React.Fragment key={data.keyValue}>
+ {children(elem)}
- {render(elem)}
</React.Fragment>
}
</dl>
);
}
(省略)
+ <ListSampleTemplate src={sampleArray} render={(elem) => (
- <ListSampleTemplate src={sampleArray}>
+ <>
+ <dt>
+ <p>{data.firstName} {data.lastName}</p>
+ </dt>
+ <>
+ )} />
- {
- <>
- <dt>
- <p>{data.firstName} {data.lastName}</p>
- </dt>
- </>
- }
- </ListSampleTemplate>
setStateを使ったstateの更新タイミング
何度も読んだ通りReactではStateを非同期に更新する。
State
が新しい値に更新されるのは、イベントハンドラーを終えた後である。
setCnt(cnt + 1); // 11
setCnt(cnt + 1); // 11
(結果) 11
常に最新の値を参照したい場合は、setState
関数に関数方の引数を渡すとよい。
setCnt(cnt => cnt + 1); // 11
setCnt(cnt => cnt cnt + 1); // 12
(結果) 12
コンポーネント間の情報引き渡し(子→親)
コンポーネント間の情報を引き渡す手段は
親→子:Props
子→親:State
となっている。
(import は省略)
export default function ParentComponent() {
// count初期化
const [count, setCount] = useState(0);
// update関数を準備
const update = (addValue) => setCount((cnt) => cnt + step);
return (
// update関数子コンポーネントへ渡す
<>
<p>合計カウント:{count}</p>
<StateCounter addValue={1} onUpdate={update} />
<StateCounter addValue={10} onUpdate={update} />
<StateCounter addValue={-1} onUpdate={update} />
</>
);
}
export default function StateCounter({ addValue, onUpdate }) {
// State(count)に加算
const handleClick = () => onUpdate(step);
return (
<button className="cnt" onClick={handleClick}>
<span>{addValue}</span>
</button>
);
}
親コンポーネントからState
を更新するためのupdate
を子コンポーネントに渡したことによって、子コンポーネントでは任意のタイミングでupdate
を呼び出しState
を更新できる。
キーイベント
ReactではJavaScriptの標準なイベントを利用できる。
(アニメーション、ドラッグアンドドロップ、マウス操作、キーボード押下などなど・・・)
キーボードイベントを発生させたい時は以下のようにする。
export default function EventHelp() {
// ctr + h でヘルプ表示
const handlreKey = (e) => {
if (e.ctrlKey && e.key === "h") {
alert("ヘルプメッセージの内容だよ");
}
};
return (
<form>
<label>
ラベル:
<input type="text" size="20" onKeyDown={handlreKey} />
</label>
</form>
);
}
<EventKey />
イベントハンドラーに任意の引数を渡す
イベントハンドラーになんらかの値を引数として渡す倍は、アロー関数を使うことによって渡すことができる。
以下の例では、アロー関数はイベントオブジェクトe
を受け取る。必要に応じて任意の引数と合わせて、本来のイベントハンドラーcurrent
に対しても引数を渡している。
イベントハンドラーに対して任意の引数を渡すこの方法は、実行時に引数の値が変化するときに使うようにする。
export default function EventSample() {
// 独自の引数を追加したイベントハンドラー
const current = (e, type) => {
const d = new Date();
switch (type) {
case "date":
console.log(d.toLocaleDateString());
break;
case "time":
console.log(d.toLocaleTimeString());
break;
default:
console.log(d.toLocaleString());
}
};
return (
<div>
{/* アロー関数経由でハンドラー呼び出し。 */}
<button id="dt" onClick={(e) => current(e, "datetime")}>
日時
</button>
<button id="date" onClick={(e) => current(e, "date")}>
日付
</button>
<button id="time" onClick={(e) => current(e, "time")}>
時刻
</button>
</div>
);
}
<EventSample />
もう一つの手法として、独自データ属性
を使った方法もある。
独自データ属性
とは、任意のタグにdata-○○○○
の形式で指定できる属性のことを指す。
独自データ属性にアクセスするときはe.target.dataset.○○
としてdataset
プロパティにアクセスする。
この手法はあらかじめ引数の値が決定しているときに使うようにする。
export default function EventSample2() {
const current = (e) => {
// datasetプロパティにアクセス
const type = e.target.dataset.type;
const d = new Date();
switch (type) {
case "date":
console.log(d.toLocaleDateString());
break;
case "time":
console.log(d.toLocaleTimeString());
break;
default:
console.log(d.toLocaleString());
}
};
return (
<div>
{/* 独自データ属性で指定 */}
<button id="dt" data-type="datetime" onClick={current}>
現在日時
</button>
<button id="dt" data-type="date" onClick={current}>
現在日付
</button>
<button id="dt" data-type="time" onClick={current}>
現在時刻
</button>
</div> );
}
非制御コンポーネント
入力値をState
で保持しないコンポーネントのこと。
入力値が欲しし場合は、<input>
や<select>
に直接アクセスする必要がある。
非制御コンポーネントは、要素オブジェクトから直接値を取得するため、対象の要素に対して参照を用意する必要がある。
その手段として、useRef
関数を使用する。
useRef(初期値)
要素に紐づける場合は、初期値をnull
とする。
const firstName = useRef(null)
return(
<intput id="firstName" ref={firstName} />
)
↑だと
1 useRef
関数からRefオブジェクト
を生成(ここでfirstName
)
2 <input (省略) ref={firstName} />
でinput
のエレメントを代入
3 Refオブジェクト
ではinput
への参照を保持
current
プロパティにアクセスすることによって、value
プロパティなどの各プロパティを取得できる。
console.log(`value: ${firstName.current.value}`)
結果
splice
Array
オブジェクトに含まれるメソッドの1つ。指定した範囲の用をそ別の要素に置き換える。
Array.splice([開始インデックス])
Array.splice([開始インデックス],削除する要素数)
Array.splice([開始インデックス],削除する要素数,追加要素・・・)
const [form, setForm] = useState({
animal: ["dog", "bigCat"],
});
// Stateに反映
const handleFormFukusuCheck = (e) => {
const fa = form.animal;
if (e.target.checked) {
fa.push(e.target.value);
} else {
fa.splice(fa.indexOf(e.target.value), 1);
}
// 編集済みの配列をStateに反映
setForm({
...form,
[e.target.name]: fa,
});
};
const show = () => {
console.log(`動物: ${form.animal}`);
};
return (
<form>
<legend>好きな動物:</legend>
<label htmlFor="animal_dog">犬</label>
<input
id="animal_dog"
name="animal"
type="checkbox"
value="dog"
checked={form.animal.includes("dog")}
onChange={handleFormFukusuCheck }
/>
<br />
<label htmlFor="animal_normalCat">猫</label>
<input
id="animal_normal_cat"
name="animal"
type="checkbox"
value="normalCat"
checked={form.animal.includes("normalCat")}
onChange={handleFormFukusuCheck }
/>
<br />
<label htmlFor="animal_bard">小さい猫</label>
<input
id="animal_small_cat"
name="animal"
type="checkbox"
value="smallCat"
checked={form.animal.includes("smallCat")}
onChange={handleFormFukusuCheck }
/>
<br />
<button type="button" onClick={show}>
送信
</button>
</form>
);
Refを使ったファイル情報を取得の例
生成したRef
オブジェクトはcurrent
プロパティで目的の要素を取得できる。
// ファイル入力ボックスへの参照
const file = useRef(null);
// ファイル情報をログ出力
function show() {
const fs = file.current.files;
// 取得したファイル群を順に出力
for (const f of fs) {
console.log(`ファイル名: ${f.name}`);
console.log(`種類: ${f.type}`);
}
}
return (
<form>
<input type="file" ref={file} multiple />
<button type="button" onClick={show}>
送信
</button>
</form>
);
結果
スプレッド構文
オブジェクトの複製を生成し分解する。
下記のコードは既存の入力値を複製し、e.target.name
だけを上書きするという意味になる。
setState({
...state,
[e.target.name]: e.target.value
})
オブジェクトを複製させたいだけなら下記のようにする。
const sampleObj = { a:'Aだよ', b:'Bだよ'}
const copyObj = {...sampleObj}
結果