React ステート管理 (TypeScript コード例)
React ステート管理 (TypeScript コード例)
ステート
ステート:コンポーネントの描画後に更新されるデータ
ステートの管理は上位コンポーネントで行う。下位コンポーネントの操作でステートを更新する場合は、 下位コンポーネントにステート更新用の関数を譲渡、それを実行することで更新を実現する。
// 親 type StarRatingProps = { totalStars?: number } const StarRating: FC<StarRatingProps> = ({ totalStars = 5 }) => { const [selectedStars, setSelectedStars] = useState(0); return ( <> {[...Array(totalStars)].map((n, i) => ( <Star key={i} selected={selectedStars > i} onSelect={() => setSelectedStars(i + 1)} // ステート更新用関数譲渡 /> ))} <p>{selectedStars} of {totalStars} stars </p> </> ) } // 子 type StarProps = { selected: boolean, onSelect: (event: React.MouseEvent<SVGElement, MouseEvent>) => void } const Star: VFC<StarProps> = ({ selected = false, onSelect = fn => fn }) => ( <FaStar color={selected ? "red" : "grey"} onClick={onSelect} /> // 操作されたらステート更新 );
フォーム
以下実現方法。制御されたコンポーネント推奨。
useRef (制御されていないコンポーネント)
- DOMノードに直接アクセスする方法
- 特徴:イミュータブルでもなければ宣言的でもない()
- 用途:React以外のライブラリとデータをやり取りする場合(はDOMに直接アクセスが必要)
type AddColorFormProps = { onNewColor: (title: string, color: string) => void } export const AddColorForm: VFC<AddColorFormProps> = ({ onNewColor = fn => fn }) => { const textTitle = useRef<HTMLInputElement>(null!); const hexColor = useRef<HTMLInputElement>(null!); const onSubmit: (event: React.FormEvent<HTMLFormElement>) => void = event => { event.preventDefault(); // デフォルト動作(submit:POST送信)抑止 const title = textTitle.current.value; const color = hexColor.current.value; onNewColor(title, color); textTitle.current.value = ""; hexColor.current.value = ""; } return ( <form onSubmit={onSubmit}> <input ref={textTitle} type="text" placeholder="input title..." required /> <input ref={hexColor} type="color" required /> <button>Add Color</button> </form> ); }
useState (制御されたコンポーネント)
- ステート経由でDOMにアクセスする
※制御されたコンポーネント内の描画関数内で重い処理の実行は避ける(パフォーマンス劣化に繋がる)
type AddColorFormProps = { onNewColor: (title: string, color: string) => void } export const AddColorForm: VFC<AddColorFormProps> = ({ onNewColor = fn => fn }) => { const [title, setTitle] = useState(""); const [color, setColor] = useState("#000000") const onSubmit: (event: React.FormEvent<HTMLFormElement>) => void = event => { event.preventDefault(); // デフォルト動作(submit:POST送信)抑止 onNewColor(title, color); setTitle(""); setColor("#000000"); } const onChangeTitle: (event: React.ChangeEvent<HTMLInputElement>) => void = event => { setTitle(event.target.value) } const onChangeColor: (event: React.ChangeEvent<HTMLInputElement>) => void = event => { setColor(event.target.value) } return ( <form onSubmit={onSubmit}> <input value={title} onChange={onChangeTitle} type="text" placeholder="input title..." required /> <input value={color} onChange={onChangeColor} type="color" required /> <button>Add Color</button> </form> ); }
カスタムフック
制御されたコンポーネントから重複したコード切り出して抽象化できる
上記コードの以下が重複を変更
value={title} onChange={event => {setTitle(event.target.value)}
interface useInputHook { (initValue: string): useInputReturns; } type useInputReturns = [ { value: string, onChange: (event: React.ChangeEvent<HTMLInputElement>) => void }, () => void ] export const useInput: useInputHook = (initValue: string) => { const [value, setValue] = useState<string>(initValue); return [ { value, onChange: event => setValue(event.target.value) }, () => setValue(initValue) ] }
// コメント: カスタムフック変更前 type AddColorFormProps = { onNewColor: (title: string, color: string) => void } export const AddColorForm: VFC<AddColorFormProps> = ({ onNewColor = fn => fn }) => { // const [title, setTitle] = useState(""); // const [color, setColor] = useState("#000000") const [titleProps, resetTitle] = useInput(""); const [colorProps, resetColor] = useInput("#000000"); const onSubmit: (event: React.FormEvent<HTMLFormElement>) => void = event => { event.preventDefault(); // デフォルト動作(submit:POST送信)抑止 onNewColor(titleProps.value, colorProps.value); // setTitle(""); // setColor(""); resetTitle(); resetColor(); } // const onChangeTitle: (event: React.ChangeEvent<HTMLInputElement>) => void = event => { // setTitle(event.target.value) // } // const onChangeColor: (event: React.ChangeEvent<HTMLInputElement>) => void = event => { // setColor(event.target.value) // } return ( <form onSubmit={onSubmit}> <input // value={title} onChange={onChangeTitle} {...titleProps} type="text" placeholder="input title..." required /> <input // value={color} onChange={onChangeColor} {...colorProps} type="color" required /> <button>Add Color</button> </form> ); }
Reactコンテキスト
- コンテキスト:中継地を経由せずにステートを伝達する方法
- コンテキストプロバイダー:データを渡す方 (親要素側)
- コンテキストコンシューマー:データを読みだす方 (利用コンポーネント側)
メリット
const Contents = () => { return ( <ThemeContext.Consumer> {theme => ( <UserContext.Consumer> {user => ( <ProfilePage user={user} theme={theme} /> )} </UserContext.Consumer> )} </ThemeContext.Consumer> ); }
ref:
※ 複数のプロバイダーがある場合、フックははるかにクリーンになる傾向がある
基本形
const colors: ColorData[] = buildInitColors(); export const ColorContext = createContext<ColorData[]>([]); ReactDOM.render( <React.StrictMode> <ColorContext.Provider value={colors}> <App /> </ColorContext.Provider> </React.StrictMode>, document.getElementById('root') );
export const ColorList: VFC<ColorListProps> = () => { const colors = useContext(ColorContext); if (colors.length === 0) { return <div>No Colors. (Add Color)</div> } return ( <div> {colors.map(color => ( <Color key={color.id} {...color.toObj()} /> ))} </div> ); }
カスタムプロバイダー
基本はコンテキスト&カスタムフックを使う=コンテキストへのアクセスがカスタムフック経由のみになり安全
コンテキスト&ステート併用
コンテキストプロバイダーはデータ変更ができないため、データ変更を可能にする手段。
type ColorProviderProps = { children: React.ReactNode } export const ColorContext = createContext(undefined!); export const ColorProvider = (props: ColorProviderProps) => { const [colors, setColors] = useState<ColorData[]>(buildInitColors()); const removeColor = (id: string) => { const excludeColor = (id: string) => colors.filter(c => c.id !== id); setColors(excludeColor(id)); } const addColor = (title: string, color: string) => { const newColors = [...colors, new ColorData(title, color)]; setColors(newColors); } return ( <ColorContext.Provider value={{ colors, addColor, removeColor }}> {props.children} </ColorContext.Provider> ); }
※setColors を公開するとどんな操作でも可能になる=バグが混入する可能性。必要な操作を行う関数のみを(value に設定して)公開する
// プロバイダー側:index.tsx ReactDOM.render( <React.StrictMode> <ColorProvider> <App /> </ColorProvider> </React.StrictMode>, document.getElementById('root') );
// コンシューマー側 export const ColorList: VFC = () => { const { colors } = useContext(ColorContext); if (colors.length === 0) { return <div>No Colors. (Add Color)</div> } return ( <div> {colors.map(color => ( <Color key={color.id} {...color.toObj()} /> ))} </div> ); }
コンテキスト&カスタムフック
カスタムフックを導入することで、コンテキストをコンシューマーに一切公開することなくデータ共有可能
コンテキストの操作をカスタムフックで隠ぺいする
type ColorContextValues = { colors: ColorData[], addColor: (title: string, color: string) => void, updateRateColor: (id: string, rating: number) => void, removeColor: (id: string) => void } const ColorContext = createContext<ColorContextValues>(undefined!); export const useColors = () => useContext(ColorContext); export const ColorProvider = (props: ColorProviderProps) => { // ... 同上のため省略 }
export const ColorList: VFC = () => { const { colors } = useColors(); if (colors.length === 0) { return <div>No Colors. (Add Color)</div> } return ( <div> {colors.map(color => ( <Color key={color.id} colorData={color} /> ))} </div> ); }
ref:
ステートが更新される=ColorProviderコンポーネント全体が再描画 -> コンポーネントツリー全体にデータ更新が反映
まとめ
ロジックをフックに分離する -> 関心の分離 = UI と ロジックを別々に開発/テスト&デプロイ可能
Golang ポインタ
Golang ポインタ
注意事項
- 自明:文字列、インターフェイス、チャネル、マップ、スライス等はそもそもポインタ
これらにポインタを使うとすれば、やむなく値無し(nil) の表現が必要な時だけ
例えば jsonデータ。存在しえないフィールドはnil代入を許容する
type JsonData struct { id string `json:"id"` nickname *string `json:"nickname,omitempty"` }
ポインタでむやみに値無しを表現するのは良くないのではないかと。 そうした場合、ポインタの表現する意味が本来のポインタの意味と値無しの2つの役割を持ってしまう。値オブジェクトにしてそのオブジェクトで値無しを表現する用にした方が良いのではないか?
- (スライスではなく) 配列の場合は値渡し
var array = [3]string{"japanese", "math", "english"} var array2 = array // 値渡し var array3 = array[:] // 参照渡し
- ポインタレシーバーでないと更新できない
setNameの中にあるpersonは、それを呼び出したインスタンスのコピー
そもそも struct がポインタ型でない場合、プロパティにアクセスするメソッドでも、インスタンスのコピーをわざわざ作り出している
type Person struct { name string } func (person Person) sayName() { fmt.Println(person.name) } func (person Person) setName(name string) { person.name = name } var hoge = Person{"Hoge"} hoge.sayName() // Hoge // インスタンスのコピー hoge.setName("Fuge") // インスタンスのコピーに対してsetするため反映されず hoge.sayName() // Hoge
- interface 実装 struct のポインタ
以下はエラー (Person does not implement Greeter (Greet method has pointer receiver)
) になる。struct
にポインタレシーバーを使用する時は、生成する際もポインタの使用必須(ポインタはポインタで合わせろと)
type Greeter interface { Greet() } type Person struct { name string } func (p *Person) Greet() { println("Hi! I'm ", p.name) } func NewGreeter(name string) Greeter { return Person{name} // return &Person{name} である必要がある } func main() { p := NewGreeter("SYM") p.Greet() }
- 再帰的な処理は深さに注意
深ければ深いほど、性能劣化に繋がる
Goでは再帰の深さが極端に処理性能が落ちる要因になります。 深さの数だけスタックを浪費する実装になっていますのでGoに慣れた人は自然と深い再帰処理はループ処理に書き換えます。(フォルダツリーのような有限な深さであることがわかっているような処理は再帰で書いても問題はありません)
- 使いすぎると性能劣化に繋がる
ポインタはアドレスを渡すだけのため効率的だが、使いすぎるとガベージコレクションに負荷がかかってしまい、多くのCPU時間を消費するようになる可能性がある
恐らくポインタでメモリを占有し続ける状態を作るとGC時に毎回見に行きコストがかかると思われる(string, map ,slice 等での定数宣言も同じことが言えるのでは?)
ref:Go のGCのオーバーヘッドが高くなるケースと、その回避策
ポインタの使い所
前提: レシーバ = メソッドの (s Square)
の部分
- ポインタを使うべきでない時
- レシーバが map、func、チャネルの場合
- レシーバがスライスで、メソッドがスライスを作りなおさない場合
- ポインタを使う時
- メソッドで値を変更する必要がある場合
- レシーバが sync.Mutex か、似たような同期するフィールドを持つ構造体の場合、コピーを避けたいデータを扱う場合
- レシーバが大きな構造体や配列の場合
- 大きな構造体をスライスに持たせる場合
ref
JS 高階関数
JS 高階関数
- 他の関数を引数に取るか
- 戻り値として関数を返すか
- もしくはその両方を満たすもの
利点
- 純粋関数 = 副作用がないため、テスト容易性/再利用性/スケーラビリティの点で優れる
元のデータの不変性を保てる
カリー化
- JSは非同期処理によりコードが複雑になりがち -> これの解消に高階関数が使われることがある
- この目的で使用するのがカリー化
const serviceLogs = serviceName => message => console.log(`${serviceName}: ${message}`); const log = serviceLogs('test'); log('Synchronous execution') promissFunc(100).then( arg => log(`Asynchronous execution (arg: ${arg})`) ).catch( err => log(`Asynchronous execution error`) ) // test: Synchronous execution // test: Asynchronous execution (arg: 100) // test: Asynchronous execution error
再起
非同期処理と組み合わせた時に真価を発揮する。再起呼び出しタイミングの調整ができる
const countdown = (value, fn, delay = 1000) => { fn(value); return value > 0 ? setTimeout(() => countdown(value - 1, fn, delay)) : value; } const outputLog = value => console.log(value); countdouwn(10, outputLog)
用途:
- データ構造の探索
- フォルダからファイル探索
- HTMLドキュメントから DOM 要素探索
- ネスト下オブジェクトとから指定のプロパティ値を取得
const obj = { type: 'person', data: { gender: 'male', info: { id: 555, name: { first: 'SYM', last: 'XXXX' } } } } deepPick('type', obj); // person deepPick('data.info.name.first', obj); // SYM const deepPick = (field, data = {}) => { [first, ...remaing] = field.split('.'); return remaing.length > 0 ? deepPick(reaming.join('.'), data[first]) : data[first]; }
関数の合成
関数の合成とは
- 関数を順番に、もしくは並行に呼び出し、複数の関数呼び出しを束ねてより大きな関数を作ることで、アプリケーション全体を構築する過程
パターン
- chaining (連鎖)
const template = 'HH:MM:SS TT'; const time = template .replace('HH', '01') .replace('MM', '23') .replace('SS', '45') .replace('TT', 'AM');
- compose (高階関数による関数合成)
関数合成関数:
const compose = (...fns) => initFunc => { fns.reduce((composed, fn) => f(composed), initFunc) }
const both = compose(civilianHours, appendAMPM); both(new Date()); // compose(civilianHours, appendAMPM) で以下となる // initFunc => { // [civilianHours, appendAMPM].reduce((composed, f) => f(composed), initFunc) // } // // both(new Date()); で initFunc = new Date() に代入され、 // [civilianHours, appendAMPM].reduce((composed, fn) => fn(composed), new Date()) // となるので、reduce で // (1) composed: new Date(), fn: civilianHours -> civilianHours(new Date()) // (2) composed: civilianHours(new Date()), fn: appendAMPM // -> appendAMPM(civilianHours(new Date())) となる
サンプル
- 命令型
setInterval(logClockTime, 1000); function logClockTime() { let time = buildClockTime(); console.clear(); console.log(time); } function buildClockTime() { const date = new Date(); let time = { hours: date.getHours(), minutes: date.getMinutes(), seconds: date.getSeconds(), ampm: 'AM' }; if (time.hours == 12) { time.ampm = 'AM' } else if (time.hours > 12) { time.ampm = 'PM'; time.hours -= 12; } if (time.hours < 10 { time.hours = '0' + time.hours; } if (time.minutes < 10) { time.minutes = '0' + time.minutes; } if (time.seconds < 10 { time.seconds = '0' + time.seconds; } }
- 関数型
- 関数は非破壊的であるため、実行後も元のオブジェクトは不変
const oneSecond = () => 1000; // 固定値を取得する時にも関数を用いる const getCurrentTime = () => new Date(); const clear = () => console.clear(); const log = message => console.log(message); const serializeClockTime = date => ({ hours: date.getHours(), minutes: date.getMinutes(), seconds: date.getSeconds() }); const civilianHours = clockTime => ({ ...clockTime, hours: clockTime.hours > 12 ? clockTime.hours - 12 : clockTime.hours }); const appendAMPM = clockTime => ({ ...clockTime, ampm: clockTime.hours >= 12 ? 'PM' : 'AM'; }); const display = target => time => target(time); const formatClock = format => time => format.replace('HH', time.hours) .replace('MM', time.minutes) .replace('SS', time.seconds) .replace('TT', time.ampm) const prependZero = key => clockTime => ({ ...clockTime, [key]: clockTime[key] < 10 ? '0' + clockTime[key] : '' + clockTime[key] }); const convertToCivilianTime = clockTime => compose( appendAMPM, civilianHours )(clockTime); const convertToDoubleDigits = civilianTime => compose( prependZero('hours'), prependZero('minutes'), prependZero('seconds') )(civilianTime); // 処理 const startTicking = () => setInterval( compose( clear, getCurrentTime, serializeClockTime, convertToCivilianTime, convertToDoubleDigits, formatClock('HH:MM:SS TT'), display(log) ), oneSecond() ); startTicking();
Python 色々メモ
Python 色々メモ
某ツール作成時に使った物をメモ(毎度期間が空いて綺麗さっぱり忘れてはググりまくるため)
reference stack
ArgumentParserを使ってpythonのコマンドライン引数をとことん使ってみた
Pythonでクラスの引数や戻り値の型アノテーションに自己のクラスを指定する
# これのみでいけた from __future__ import annotations
[Python] インスタンスのプロパティへ動的にアクセスする
c = Container() setattr(c, "key", "value") print(c.key) # => value val = getattr(c, "key") print(val) # => value
date 系
Pythonで日付文字列からのdatetime変換やタイムゾーンの変更などをいい加減覚えたい
Pythonで文字列 <-> 日付(date, datetime) の変換
from datetime import datetime as dt # 文字列 → 日時 tstr = '2012-12-29 13:49:37' tdatetime = dt.strptime(tstr, '%Y-%m-%d %H:%M:%S') # 日時 → 文字列 tdatetime = dt.now() tstr = tdatetime.strftime('%Y/%m/%d %H:%M:%S')
文字列系
Pythonで文字列を比較(完全一致、部分一致、大小関係など)
Pythonの文字列フォーマット(formatメソッドの使い方)
# 書式設定 (インデックス番号は省略可) "{0:<20,.3f}".format(12345.67) # '12,345.670' "{:<20,.3f}".format(12345.67) # '12,345.670' # int型:左に空白 "{:2d}".format(100) # '100' "{:10d}".format(100) # ' 100' # ゼロパティング "{:010d}".format(100) # '0000000100' # 小数点以下 "{:.1f}".format(0.01) # '0.0' "{:.2f}".format(0.01) # '0.01' "{:.5f}".format(0.01) # '0.01000' # 指数表記 "{:.1e}".format(0.01) # '1.0e-02' "{:.2e}".format(0.01) # '1.00e-02' "{:.5e}".format(0.01) # '1.00000e-02' "{:.5E}".format(0.01) # '1.00000E-02' # str型:右に空白 "{:2}".format("abcde") # 'abcde' "{:10}".format("abcde") # 'abcde ' # 右寄せ "{:>20}".format("abcde") # ' abcde' "{:*>20}".format("abcde") # '***************abcde' # 最大文字数指定 "{:.3}".format("abcde") # 'abc' "{:.7}".format("abcde") # 'abcde'
Pythonで文字列を置換(replace, translate, re.sub, re.subn)
# 複数の文字を指定して置換 s = 'one two one two one' print(s.translate(str.maketrans({'o': 'XXX', 't': None}))) # XXXne wXXX XXXne wXXX XXXne
Pythonで文字列の先頭と末尾から空白や文字列を削除する:strip()
# 先頭/末尾削除 "Hello World World Hello".strip('Hello') # " World World " # 先頭/末尾の空白/改行削除 s = " Hello World\n".strip() # "Hello World" # 先頭のみ削除 s = " Hello World\n".lstrip() # "Hello World\n" # 末尾のみ削除 s = " Hello World\n".rstrip() # " Hello World"
List
Pythonでリストをソートするsortとsortedの違い
Dictionary
Pythonで辞書を作成するdict()と波括弧、辞書内包表記
# 辞書内包表記 l = ['Alice', 'Bob', 'Charlie'] d = {s: len(s) for s in l}
d1 = {'k1': 1, 'k2': 2} d2 = {'k1': 10, 'k3': 3, 'k4': 4} # 別オブジェクトで生成 d = d1 | d2 # {'k1': 10, 'k2': 2, 'k3': 3, 'k4': 4} # 上書き d1 |= d2 # {'k1': 10, 'k2': 2, 'k3': 3, 'k4': 4}
ファイル系
[Python] フォルダやファイルのコピー、移動、削除(shutilモジュール)
Pythonでファイル・ディレクトリを削除するos.remove, shutil.rmtreeなど
import shutil # フォルダ丸ごとコピー (既にフォルダがあると失敗) shutil.copytree('./sample', './sample_backup') # フォルダ丸ごと削除 shutil.rmtree()
# フォルダ内の特定ファイル名のみ取得 import os files = os.listdir('Folder\\') extension = '.txt' text_files = [file for file in files if extension in file] for file_name in text_files: print(file_name)
# フォルダ内の特定ファイルフルパス取得 import glob extension = '.txt' text_files = glob.glob('Folder\\*' + extension) for file_path in text_files: print(file_path)
Pythonでファイルのタイムスタンプ(作成日時や更新日時)を取得
# 最終アクセス日時 print(os.path.getatime('/temp/test.txt')) # = os.stat('temp/test.txt')).st_atime # 最終内容更新日時 print(os.path.getmtime('/temp/test.txt')) # = os.stat('temp/test.txt')).st_mtime # メタデータの最終更新日時(UNIX) / 作成日時(Windows) print(os.path.getctime('/temp/test.txt')) # = os.stat('temp/test.txt')).st_ctime # 作成日時(macOSを含むFreeBSD系の一部のUNIXのみ) # os.stat('temp/test.txt')).st_birthtime
PythonでWeb上の画像などのファイルをダウンロード(個別・一括)
import os import urllib.error import urllib.request def download_file(url, dst_path): try: with urllib.request.urlopen(url) as web_file, open(dst_path, 'wb') as local_file: local_file.write(web_file.read()) except urllib.error.URLError as e: print(e) def download_file_to_dir(url, dst_dir): download_file(url, os.path.join(dst_dir, os.path.basename(url))) url = 'xxxxxx' dst_dir = 'data/temp' download_file_to_dir(url, dst_dir)
XML 操作系
【Python】XMLのデータを読み取り・書き込みする(ElementTree)
【Python】XMLを解析・読み取りする(ElementTree)
ElementTreeやlxmlで名前空間を含むXMLの要素を取得する
フロントエンド 技術要素概要集
フロントエンド 技術要素概要集
※随時追加
SPA (Single Page Application)
- | 特徴 |
---|---|
Not SPA | サーバとのやり取りが同期的。ユーザ操作->ページ全体更新(ページ遷移) |
SPA | サーバとのやり取りが非同期的。ユーザ操作->部分更新が可能 |
※いつか追記
CSR (Client Side Rendering)
- ユーザーのブラウザで全てレンダリングする方法
- 空のHTMLとレンダリングするためのjsやCSSを受け取り、jsで描画のためのCSSとHTMLを生成、最終的なDOM/CSSMOMをもとにブラウザが描画
- サーバへの負荷が少ない分、ユーザーサイドに負荷を強いる
- 最初は空のHTMLしかないためSEO的に不利
SSR (Server Side Rendering)
- サーバー側で JavaScript を実行し、生成結果のHTML,CSS,jsをユーザー側に返すため、ユーザー側の処理能力に依存しない
- サーバーがリクエストを受けた時点で HTML を生成し、それをブラウザに返す
- (ページを開いてからHTMLなどの生成を行うため)初期描画が遅い
CSRだとユーザのリソースに依存するし、ブラウザでのレンダリング処理速度にも限界はある。規模が大きくなればなるほど描画に時間がかかる->UX低下に繋がるから、サーバ側でできることはサーバ側で行うことでユーザ(クライアント) サイドの負荷軽減できるでしょう。
SSG (Static Site Generation)
- ビルド時に全ての情報を取得して静的ファイルを作る(正確にはビルド時に静的なページを生成する機能を指す)
- ビルド時に(APIで取得するコンテンツ等も含めて)全ての情報を取得してビルドするため、コンテンツ量が増えるとビルド時間も増える
- キャッシュをすることができて、CDNを通して世界中に配信できるように
- 頻繁に情報を更新するようなサイトには不向き(静的サイト向け)
- ページ数の多いサイトはページの追加や内容の更新時に事前ビルドのコストが大きくなる
静的なものなら、都度サーバサイドでのレンダリングすらする必要なくて、ビルド時に完結させ、CDNに配置することで、役割分担/負荷分散にもなるでしょう。
ISR (Incremental Static Regeneration)
- 増分静的再生成
- アクセスされた時に初めてSSGのビルドが走りサイトを生成する方法(2回目以降はビルド済みなため結果のみを返す)
- ビルド後に静的生成されたページの追加/更新ができる(動的に静的ページを更新可能に)
一度に全部ビルドしていては規模が大きくなればビルド時間が長くなるので、必要な時に必要な部分だけビルドしよう。そうすることでサイトの更新自体もスムーズにできるでしょう。
Remix
React フレームワーク
特徴
- サーバー/クライアントモデル
- 基盤となる技術を抽象化しないこと
- ウェブの基礎となるブラウザ、HTTP、そしてHTMLなどと共に取り組んでいく
- JavaScriptを使ってブラウザの動作をエミュレート(システムの挙動を別のソフトウェアなどによって模倣し、代替として動作させる)し、UXを向上させる
SSG,ISRは、(静的サイトを作るための手法故に) ビルド時間、ビルドの複雑さ、サイトの変更をするまでのタイムラグ、認証が必要なサービスには使用不可などその他様々な問題がある。
そのため、Next.jsやGatsbyとは路線が異なり、SSGなど予めデータを用意して高速化を測る手法をとらず、サーバーサイドレンダリングやブラウザのFetchAPIを活用してデータをやり取りするといった手法をとっている模様。
よりサーバサイドにを仕事を担わせることで、よりパフォーマンス最適化を行えるようにしようとしている?
ref
ref
React スタイリング メモ
React スタイリング メモ
React スタイリング 手段
手法は大きく2つに分かれていそう
ダイナミックスタイルが必要になるなら、CSS-in-JS。ただしCSS-in-JSはライブラリ等への依存度高い上、CSSをそのまま移植はできない。CSS Modulesで書くCSSは標準技術であり、CSSだけは結合度を低くできるから移植もしやすい。安定を取るならCSS Modules。
手段
- CSS Modules
Vanilla-Extract (CSS Modules)
Styled Component (CSS-in-JS)
emotion (CSS-in-JS):メリットやデメリットはstyled-componentsとほぼ一緒
Material UI (UI ライブラリ)
Chakra UI (UI ライブラリ)
- スタイルを限られたパラメータから選んで指定するアプローチで、UIに一貫性を持たせやすくしたライブラリ
- デフォルトのスタイルに癖がなく、ユーティリティファーストでプロップから簡単にカスタマイズできる。
- emotion を利用している模様 (=CSS-in-JSの特徴に当てはまる?)
- ref: ReactのUIコンポーネントライブラリ「Chakra UI」とは? カスタマイズ性と生産性を両立しよう【前編】
非推奨:
- styleプロップ:パフォーマンスが悪い
ref
エンジニアを分類する3タイプから考える自身のタイプとその先と
※後日修正予定
エンジニアを分類する3タイプから考える自身のタイプとその先と
以下を多分に引用して記す。
「技術の人は技術の本質を追求しテックリード/アーキテクト、プロダクトの人は技術を手段と割り切りフルスタックエンジニア/PdM、組織の人は開発生産性を高めようとEM/PMOを目指すことが多い」とのこと。そのことについて突っ込んで書かれた記事。
3タイプ
3タイプについて見やすくまとめる
前提:エンジニアとしての志向性を以下3つに分類すると目指すキャリアが考えやすいと言う話
- ※この3つのタイプはスキルの絶対量ではなく、何を「目的」とし、何を「手段」とするかというスタンスの違いとのこと
- ※どの志向性が自分は強そうかというバランスとしてとらえておくと良い
3タイプとその特徴等まとめると以下
- 技術が好きな人
- プロダクトが好きな人
- 目的:良いプロダクトをつくりユーザーに価値提供すること
- 手段:技術が手段
- 趣向:自分のつくりたい機能をまるっと実装したい(技術領域はフルスタックに広く浅くなりやすい傾向) 、UXやUIの領域やデータ分析などにも強いこだわりを持つ、どう実装するかよりもなぜ何をつくるか/どう改善していくのかを考えるようになる
- 組織が好きな人
自身のタイプと特性
どれにも当てはまるバランス型…
タイプ的には、プロダクト好きが中心ではあるが、プロダクト開発の手段として必要となる技術やチーム(組織)も重視している。比率的には、「技術:プロダクト:組織 = 3.5:4:2.5」と考える(どちらかと言えば技術>組織のため)。
そう考えた詳細は以下の通り。
- 技術の本質を追求したい
- 引用記事にある
数学的なアルゴリズムやバイナリ・プロトコルなど低レイヤの技術/プログラム言語そのもの/個々のフレームワークというよりもアーキテクチャの考え方自体に興味を持ちます
やチームの心理的安全性やカルチャーは重視しているが、非合理な人の感情と向き合うことは好きではなく
にがっちり当てはまる。
- 引用記事にある
- が、技術は(プロダクトなど物を作る上での)手段と割り切っている = プロダクト指向
- それ故か
自分のつくりたい機能をまるっと実装したい
やフルスタック足りたいと考えている。 自分がコードを書かなくてもいい
とまでは思ってない。できれば書きたいが、必ずしも自身が書かなくても良い位の温度感(常にコードが書けるだけの実力は持っておき、必要な場面があれば自身がコードを書くor書けるようにしておきたいと思っている。≒サポート思考)
- それ故か
- が、
使用する技術やビジネス価値/事業インパクトそのものには深く踏み込まない
には当てはまる- 使用する技術に強いこだわりはないように思う(が全くない訳ではなく、プロダクトや開発組織/チームの状態や特性等を踏まえて適切な物を選びたい欲を持っている) 。目的を果たせるなら細かい手段までは気にしないが、ある程度プロダクトにとって適切なものを選びたい
- ビジネス価値/事業インパクトに関しては、そこを強く意識する機会が無かったため、正確にはまだ分かっていないだけかもしれない。(※詳細後述)
- また、
高い開発生産性を持つ優れた開発チームつくること
にも関心がある- (2022/3現在) 現職にて自身が属するチーム(6名程)のチームの心理的安全性が低く、理想とはかけ離れた状態が故、制約の多い中少しでも良くできないかと試行錯誤し続けている。そういった実体験があり、プロダクト開発に立ち向かう上で欠かせない要素だと強く肌で感じているため(※詳細後述)
上記を踏まえつつ、先を考えるため、現状を整理する(自身の特性含む)(2022/3、31歳時)。
- SIer業界2次請けサイドSE。別会社のプロダクト開発に従事(2019/12~参画中)
- 現状(なんちゃって)フルスタック、サブチームのサブリーダー
- プロダクトの全体~詳細を見ていけるだけの力がある(それだけ既存物への理解力が高い)
- 4~5システムが連携することで1つのサービスを形成する大規模プロダクトのうち2システムの改修に携わった。
- 携わった一方はマイクロサービスアーキテクチャで、(触れてない部分もあるため全てではないが)おおよそ各パーツの役割とどう連携しているかを掴んでいる
- もう一方は、6ヶ月前から開発に携わるようになったものであり、中身の詳細を着実に掴んでいき、実装面ではサブチームのメンバー全員を牽引しているレベル
- 各システムの役割をしっかりと掴み、新規参画メンバーにプロダクトの全体アーキテクチャの概要や、どうやってサービスを実現しているのか、ユーザ視点を交えて各システムがどういったことができるのか/機能を提供しているかを説明。
- 2次請けサイド故にプロダクトとの距離が遠い。
- プロダクトの価値、役割、位置づけ等、概要的なことは押さえているが、実際のユーザからどういった要望/フィードバックが来ているのか全く分からない。知ることができない。
- チームの不足部分を埋めるような動きをする
- 所属サブチームで自身のみが突出しておりサポートに回るばかり≒サポート型
- 所属サブチームの心理的安全性や、開発生産性が低いと感じている=不足部分をなんとか改善できないかを試行錯誤している
現状もやもやしている点&先のキャリアへの考えは以下
- どちらかと言えばプロダクト中心指向にも関わらずプロダクトとの距離感が遠いこと(解消には転〇必須)
- 本音はフルスタックを目指したいとどこかで思っている
- が、これまでの経験/スキル/年齢的にも技術/エンジニア一本の方向は厳しい。自身の地頭的にも
- 仮にそちらに倒すにしてもスタートアップが狙い目になるのかと思うが、0->1、1->10は経験がない上、不得意なため相性の問題から止めた方が良い。
- (消去法的に)プロダクトor組織の方向かと考える
- マネージャーを目指すのは良しと考えているが、上記にある自身の特性がより活きる方向はどの方向か
- プロダクトコードとの距離が密接な方が自身の真価を発揮できると思われるし、将来的には少しでも手を動かしていたいため、プレイングマネージャー的な所を目指したいと考えている(プロダクト/組織の間?)。果たしてそんなポジションあるのか
キャリアに関しては、こだわるつもりはなく、(カメレオンタイプの気質もあるように思うため)、転〇先の状態等を考慮して柔軟に考えていきたい。ただ、確実に問われる部分ではあるため、ある程度整理した。
※気が向いたら適宜更新
以上