メインコンテンツまでスキップ

React 19 Ref

· 約3分
Mikyan
白い柴犬

In React 19, ref can directly be passed to a component as a property, and forwardRef become unrecommanded.

About ref

In React, a ref (reference) let you operate lower-level imperative API on the following elements:

  • DOM nodes
  • Class component instances

Usually it is created with useRef hook.

function Chat() {
const endRef = useRef();
useEffect(() => {
endRef.current.scrollIntoView({ behavior: 'smooth' });
}, [messages]);
return (
<div className="chat-window">
{messages.map(m => <Message key={m.id} />)}
<div ref={endRef} />
</div>
);
}

Sometimes we need to pass ref down to point to one component of a child component, below are suitable situations:

  • When you build a HOC component
  • When you wrapping a core component with customized design (like wrapping a material UI Input)

Note that usually it is not a good practice, since it increase the complicity and voilate the capsluating philosoph, we should be careful about the usecases.

Improved ref API in React.19

Before React 19, it is not allowed to pass ref as a property, Instead, forwardRef is needed:

import React, { forwardRef, useRef, useImperativeHandle } from 'react';

const FancyInput = forwardRef(function FancyInput(_, ref) {
const inputRef = useRef();

useImperativeHandle(ref, () => ({
focus: () => inputRef.current?.focus(),
clear: () => { if (inputRef.current) inputRef.current.value = ''; }
}), []);

return <input ref={inputRef} className="border rounded p-2" />;
});

export default FancyInput;

// Parent.jsx
import React, { useRef } from 'react';
import FancyInput from './FancyInput';

function Parent() {
const fancyRef = useRef();

return (
<>
<FancyInput ref={fancyRef} />
<button onClick={() => fancyRef.current.focus()}>Focus</button>
<button onClick={() => fancyRef.current.clear()}>Clear</button>
</>
);
}

Note in the example, useImperativeHandle is used to defines the customer handler of ref. By declaring useImperativeHandle in the component, we can:

  • rewrite the ref handler behavior
  • safely limit the handlers of the ref

By using useImperativeHandle, the original ref donot directly pass to the input, instead in the component we define a new ref, to let the ref operates indirectly.

However, from React 19, forwardRef is not recommended, and ref can be passed directly, which is more concise and have better readability:

import React, { forwardRef, useRef, useImperativeHandle } from 'react';

function FancyInput({ ref }) {
const inputRef = useRef();

useImperativeHandle(ref, () => ({
focus: () => inputRef.current?.focus(),
clear: () => { if (inputRef.current) inputRef.current.value = ''; }
}), []);

return <input ref={inputRef} className="border rounded p-2" />;
});

export default FancyInput;

// Parent.jsx
import React, { useRef } from 'react';
import FancyInput from './FancyInput';

function Parent() {
const fancyRef = useRef();

return (
<>
<FancyInput ref={fancyRef} />
<button onClick={() => fancyRef.current.focus()}>Focus</button>
<button onClick={() => fancyRef.current.clear()}>Clear</button>
</>
);
}

Java Optional Best Practices

· 約6分
Mikyan
白い柴犬

JavaのOptionalは、値が存在する場合も存在しない場合もあるコンテナオブジェクトです。これにより、nullチェックの手間を減らし、メソッドの戻り値として「値があるかどうか」を明示的に伝えることができます。Optionalは以下のような利点があります:

明確な意図: メソッドが値を返さない可能性があることを示す。 安全な操作: 明示的なnullチェックを回避し、NullPointerExceptionのリスクを減少させる。 関数型スタイル: map, filter, flatMap などを活用したチェーン処理が可能。

Optionalの作成

Optionalは主に2つのファクトリーメソッドを使って生成します。

  • Optional.of(T value):
    • 値が必ず非nullであることを期待します。
    • nullを渡すと、NullPointerExceptionが発生します。
  • Optional.ofNullable(T value):
    • 値がnullの場合は空のOptional(Optional.empty())を返し、非nullの場合は値を保持します。

作成例:

// Method that processes an input string and returns an Optional.
public static Optional<String> processValue(String input) {
// Return empty if input is null or blank
if (input == null || input.trim().isEmpty()) {
return Optional.empty();
}
// Otherwise, return a trimmed value wrapped in an Optional
return Optional.of(input.trim());
}

Optionalの利用方法

Optionalの利用は主に以下のユースケースに分類されます。

  1. 値の存在チェック
    • isPresent() / isEmpty():
      • 値が存在するかどうかを確認する。
      • 例: if (optional.isPresent())
  2. 値の取得
    • get():
      • 値が存在する場合に返すが、存在しない場合はNoSuchElementExceptionをスローします。
      • 注意: 直接get()を使うのはリスクがあるため、他のメソッドと組み合わせることが望ましい。
    • orElse(defaultValue):
      • 値が存在しない場合にデフォルト値を返します。
    • orElseThrow(exceptionSupplier):
      • Optionalが空の場合に、指定した例外をスローします。
  3. 値の処理
    • ifPresent(Consumer):
      • 値が存在する場合に、Consumer(値を受け取って何らかの処理を行うラムダ式)を実行します。
    • ifPresentOrElse(Consumer, Runnable):
      • 値が存在する場合はConsumerを、存在しない場合はRunnable(引数を取らない処理)を実行します。
  4. Optionalのチェーン処理
    • map(Function):
      • 値が存在する場合に、Functionを適用し結果を新しいOptionalとして返します。
    • filter(Predicate):
      • 値が条件を満たすかどうかをチェックし、満たさない場合は空のOptionalを返します。
    • flatMap(Function):
      • ネストされたOptionalの解除に利用します。
    • or(Supplier):
      • Optionalが空の場合に、新たなOptionalを返すために利用します。

利用例:

String value1 = "  Hello, World!  ";
Optional<String> opt1 = processValue(value1);

// 存在チェック
if (opt1.isPresent()) {
System.out.println("Value exists!");
} else {
System.out.println("Value is empty.");
}

// 値の取得
try {
// 値がなければ例外がスローされる
String result = opt1.orElseThrow(() -> new IllegalStateException("Value is empty"));
System.out.println("Result: " + result);
} catch (IllegalStateException ex) {
System.err.println(ex.getMessage());
}

// デフォルト値を利用
String defaultResult = opt1.orElse("Default Value");
System.out.println("Default Result: " + defaultResult);

// 値の処理
opt1.ifPresent(val -> System.out.println("Processed value1: " + val));

// ifPresentOrElseを使用して、存在する場合は処理し、存在しない場合は別の処理を実行
opt1.ifPresentOrElse(
val -> System.out.println("Processed value1: " + val),
() -> System.out.println("Value is empty")
);

// チェーン処理:変換とフィルタ
Optional<Integer> lengthOpt = opt1
.map(String::toUpperCase) // 値を大文字に変換
.filter(val -> val.length() > 10) // 長さが10より大きいかフィルター
.map(String::length); // 文字列の長さを取得

lengthOpt.ifPresent(len -> System.out.println("Length of processed value: " + len));

// orを使用して、Optionalが空の場合に代替値を提供
Optional<String> opt2 = opt1.or(() -> Optional.of("Default Greeting"));
System.out.println("Result from opt2: " + opt2.get());

Optionalのベストプラクティス

  1. 戻り値としての利用:

    • Optionalはメソッドの戻り値に利用し、値が存在しない可能性を明示する。
  2. メソッド引数としては使用しない:

    • Optionalを引数にすると、呼び出し側に無駄なラッピングを強いるため、シンプルなnullチェックやオーバーロード、あるいは別のパラメータ設計を検討する。
  3. 直接get()の使用を避ける:

    • 値の取得はorElse, orElseThrow, ifPresentなどのメソッドを利用して、安全に扱う。
  4. 不必要なオブジェクト生成を避ける:

    • チェーン操作を利用して、コードをシンプルに保つ。

Git Cheat Sheet

· 約3分
Mikyan
白い柴犬

問題の背景

チームで開発する際、よくあるパターンとして、

  • developブランチから、featureブランチAを切って実装、PullRequestを作成し、レビュー待ち
  • その間に、featureブランチAにfeatureブランチBを切って、新しい機能を実装します
  • featureブランチAのレビューを経て承認され、スカッシュマージによってdevelopブランチへ統合され
  • この時、featureブランチBのコミットがfeatureブランチAの元のコミットに含まれてしまうことがあって、developブランチと多くのマージコンフリクトが発生してしまいました。

問題発生の原因

  • スカッシュマージによって個々のコミットがまとめられたため、ブランチBには依然としてブランチAの個々の変更が残っていること
  • その結果、developブランチに既に存在するスカッシュコミットとブランチBの変更が重複して適用され、コンフリクトが発生したこと

解決策:リベースによる解決

ブランチBをブランチA由来のコミットを取り除いて、developブランチ上のスカッシュコミットに合わせてリベースすることで、効率よく解決できます。

具体的には、以下の手順で行います:

git checkout feature/branch-b
git rebase --onto develop HASH_BASE feature/branch-b

このコマンドは、ブランチ B の中で HASH_BASE より後のコミットだけを、develop ブランチ上に再適用します。 HASH_BASEは、ブランチ A が develop から分岐した時点のコミットハッシュにすると、元ブランチ A のコミットがブランチ B に含まれなくなります。

リベース中にコンフリクトが発生した場合、各コンフリクト箇所で適切に修正し、 以下のコマンドでリベースを続行します。

git add <修正済みファイル>
git rebase --continue

リベースが正常に完了したら、ブランチ B の変更を develop ブランチへPRを作成してマージしましょう。

この方法により、履歴がクリーンになり、将来的なマージやデバッグ作業が大幅に楽になります。

意思決定の全てはROI

· 約2分
Mikyan
白い柴犬

複数人が関わっているプロジェクトのやり方、難易度はグッと上がるです。 なぜなら、人々のコミュニケーション、すり合わせ、タスク間の依存関係、を考えなければならないです。しかもこれらのプロジェクトのインパクトが高い、できれば大きな利益が出る、うまくできないと大きな損失が出る可能性があるのできちんと管理する必要があります。

チームでのプロジェクトの進め方は、個人プロジェクトの管理方法に加えて、以下三つの要点があります:

  • プロジェクトを分解をする
    • MECEで分解
    • 前後依頼を確定
  • 各タスクを関与する人を確定、評価するかどうかを確定
  • 各マイルストーンの確認時点を定める

プロジェクト管理についての理解1、一人プロジェクト

· 約5分
Mikyan
白い柴犬

これまでいくつかの会社で、さまざまなプロジェクトの管理やリードに関わってきました。
その中で得た知見を、将来の自分のためにも簡単にまとめておきたいと思います。

プロジェクトとは、「限られたリソースを使い、限られた期間で、特定の目的を達成する取り組み」です。
その目的を実現するためには、適切な管理が欠かせません。

プロジェクトの規模によって管理方法は大きく異なるため、私は以下の3つに分類できると考えています。

  • 一人プロジェクト
  • チームプロジェクト
  • 複数組織が関わるプロジェクト

今回はこの中でも、「一人プロジェクト」の管理についてまとめてみます。


一人プロジェクトとは?

プロジェクトの定義を広く捉えれば、さまざまな取り組みがプロジェクトとして扱えます。
たとえば、以下のようなものも一人プロジェクトに該当します。

  • 個人旅行の計画
  • ブログの運営
  • 上司に依頼された勉強会の企画

一人で進めるプロジェクトは、特に管理が軽視されがちです。
単なるタスクのように感覚的に進めてしまうことも多いでしょう。

しかし、「これはプロジェクトだ」と意識し、適切に管理することで、より楽しく、より良い結果を得ることができます。


一人プロジェクト管理で意識すべき3つのポイント

1. 目的を明確にする

  • 「なぜそれをやるのか?」を繰り返し問いかけ、問題を言語化します。
  • 曖昧なまま進めると、途中で迷子になったり、やり直しが発生したりします。

特に他人から頼まれたプロジェクトでは、目的が明確にされていないことが多いです。

例:勉強会の幹事を任された場合

  • チームのコミュニケーションを活性化するため?
  • AIなど最先端技術を学ぶ場として?
  • プレゼン能力を伸ばすため?
  • チームカルチャーを育てるため?

目的によって、トピック選定、参加メンバー、運営方針がすべて変わってきます。
だからこそ、最初に目的を明確にすることが最重要です。


2. マイルストーンに分解する

  • タスクが小さく見えても、実際は難しい部分や不確実な要素が隠れていることがあります。
  • あらかじめ全体像を把握し、マイルストーン(節目)に分解しておくことで、計画的に進めることができます。

また、マイルストーンは進捗の報告タイミングとしても活用できます。


3. 報連相(報告・連絡・相談)を怠らない

一人で進めるからこそ、「見える化」が重要です。
上司や依頼者に対して、今どこまで進んでいるか・どんな課題があるかを適切に共有しましょう。

これには以下のメリットがあります:

  • 認識違いがあっても、早い段階で軌道修正できる
  • 環境や要件の変化にも柔軟に対応できる
  • 自分の成果が評価されやすくなる

おわりに

一人で動くプロジェクトこそ、意識的に「プロジェクト」として扱うことで成果が変わってきます。

  • 目的の明確化
  • マイルストーンの設計
  • 可視化と報連相

この3つを意識すれば、小さな取り組みでも効果的に、そして気持ちよく進めることができるでしょう。

How to setup SFTP in EC2 allowing file upload

· 約2分
Mikyan
白い柴犬

SFTP use SSH protocal, allowing user to transfer files safely. It is a subsystem of SSH, so it runs in port 22.

The name contains FTP, however it is not implements FTP protocal

  • It achieves the same function with FTP
  • It is widely supported by FTP clients

When you want to transfer files with server, it might be a good choice.

How to setup a sftp user allowing it to upload files into specific folder

The following scripts helps setup a SFTP user in

Save it to .sh file, execute the following commands

chmod +x setup_sftp_user.sh
sudo ./setup_sftp_user.sh vendor

It will prompt you to set the password and configure the rest automatically.

#!/bin/bash

# Check if running as root
if [ "$EUID" -ne 0 ]; then
echo "Please run as root or use sudo"
exit 1
fi

# Check if username is provided
if [ -z "$1" ]; then
echo "Usage: $0 <username>"
exit 1
fi

USER=$1

# Create user
useradd -m $USER
passwd $USER

# Setup SFTP directories
mkdir -p /var/sftp/$USER/uploads
chown root:root /var/sftp/$USER
chmod 755 /var/sftp/$USER

chown $USER:$USER /var/sftp/$USER/uploads
chmod 755 /var/sftp/$USER/uploads

# Backup sshd_config
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak

# Add SSH config for user
cat <<EOL >> /etc/ssh/sshd_config

Match User $USER
ChrootDirectory /var/sftp/$USER
ForceCommand internal-sftp
AllowTcpForwarding no
X11Forwarding no
EOL

# Restart SSH daemon
systemctl restart sshd

# Confirm success
echo "SFTP user '$USER' has been set up successfully."