
Reactをセットアップするための環境設定
はじめに React(リアクト)は、Facebook(現Meta)が開発したJavaScriptライブラリで、主にWebアプリケーションの**ユーザーインターフ […]
class Button extends React.Component {
handleClick() {
console.log('Button clicked');
}
render() {
return <button onClick={this.handleClick}>Click me</button>;
}
}
useEffect(() => {
console.log('Effect ran');
}, []);
// 無限ループの例
function InfiniteLoop() {
const [count, setCount] = useState(0);
useEffect(() => {
setCount(count + 1); // state更新が再レンダリングを引き起こし、useEffectが再実行される
}); // 依存配列なし
return <div>Count: {count}</div>;
}
function ClickButton() {
const handleClick = () => {
console.log('Clicked!');
};
return <button onClick={handleClick}>Click me</button>;
}
class Button extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log('Button clicked');
}
render() {
return <button onClick={this.handleClick}>Click me</button>;
}
}
// またはアロー関数を使用
class Button extends React.Component {
handleClick = () => {
console.log('Button clicked');
};
render() {
return <button onClick={this.handleClick}>Click me</button>;
}
}
function ClickCounter() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
};
return (
<div>
<button onClick={handleClick}>Click me</button>
<p>Clicked {count} times</p>
</div>
);
}
function MyComponent() {
useEffect(() => {
console.log('Component mounted');
}, []);
return <div>My Component</div>;
}
function MyComponent() {
useEffect(() => {
return () => {
console.log('Component unmounted');
};
}, []);
return <div>My Component</div>;
}
function TextInput() {
const [value, setValue] = useState('');
const handleChange = (e) => {
setValue(e.target.value);
};
return (
<input type="text" value={value} onChange={handleChange} />
);
}
function Checkbox() {
const [checked, setChecked] = useState(false);
const handleChange = (e) => {
setChecked(e.target.checked);
};
return (
<input
type="checkbox"
checked={checked}
onChange={handleChange}
/>
);
}
function SelectBox() {
const [selectedValue, setSelectedValue] = useState('');
const handleChange = (e) => {
setSelectedValue(e.target.value);
};
return (
<select value={selectedValue} onChange={handleChange}>
<option value="">Select an option</option>
<option value="option1">Option 1</option>
<option value="option2">Option 2</option>
<option value="option3">Option 3</option>
</select>
);
}
// 親コンポーネント
function Parent() {
const handleChildClick = () => {
console.log('Child button clicked');
};
return <Child onClick={handleChildClick} />;
}
// 子コンポーネント
function Child({ onClick }) {
return <button onClick={onClick}>Click me</button>;
}
function EnterKeyHandler() {
const handleKeyDown = (e) => {
if (e.key === 'Enter') {
console.log('Enter key pressed');
}
};
return ;
}
function Tooltip() {
const [isVisible, setIsVisible] = useState(false);
return (
<div>
<button
onMouseOver={() => setIsVisible(true)}
onMouseOut={() => setIsVisible(false)}
>
Hover me
</button>
{isVisible && <div className="tooltip">Tooltip content</div>}
</div>
);
}
e.stopPropagation()
を使用します。
function StopPropagation() {
const handleClick = (e) => {
e.stopPropagation();
console.log('Button clicked, but event won't bubble up');
};
return (
<div onClick={() => console.log('Div clicked')}>
<button onClick={handleClick}>Click me</button>
</div>
);
}
function useEventListener(eventName, handler) {
useEffect(() => {
window.addEventListener(eventName, handler);
return () => {
window.removeEventListener(eventName, handler);
};
}, [eventName, handler]);
}
// 使用例
function ResizeListener() {
const [windowSize, setWindowSize] = useState({
width: window.innerWidth,
height: window.innerHeight
});
useEventListener('resize', () => {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight
});
});
return (
<div>
Window size: {windowSize.width} x {windowSize.height}
</div>
);
}
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(response => response.json())
.then(data => setUser(data));
}, [userId]);
return <div>{user ? user.name : 'Loading...'}</div>;
}
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCount(prevCount => prevCount + 1);
}, 1000);
return () => clearInterval(interval);
}, []);
return <div>Count: {count}</div>;
}
function LifecycleDemo() {
const [count, setCount] = useState(0);
// マウント時のみ
useEffect(() => {
console.log('Component did mount');
}, []);
// 更新時(countが変更された時)
useEffect(() => {
console.log('Count updated:', count);
}, [count]);
// アンマウント時
useEffect(() => {
return () => {
console.log('Component will unmount');
};
}, []);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
function useDocumentTitle(title) {
useEffect(() => {
document.title = title;
}, [title]);
}
// 使用例
function PageTitle() {
const [count, setCount] = useState(0);
useDocumentTitle(`Count: ${count}`);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
function FixedLoop() {
const [count, setCount] = useState(0);
useEffect(() => {
if (count < 10) {
setCount(count + 1);
}
}, [count]); // countを依存配列に追加
return <div>Count: {count}</div>;
}
function MultiInputForm() {
const [formData, setFormData] = useState({ firstName: '', lastName: '', email: '' });
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({ ...prev, [name]: value }));
};
return (
<form>
<input name="firstName" value={formData.firstName} onChange={handleChange} placeholder="First Name" />
<input name="lastName" value={formData.lastName} onChange={handleChange} placeholder="Last Name" />
<input name="email" value={formData.email} onChange={handleChange} placeholder="Email" />
</form>
);
}
function ValidatedForm() {
const [formData, setFormData] = useState({ email: '', password: '' });
const [errors, setErrors] = useState({});
const validate = () => {
const newErrors = {};
if (!formData.email) newErrors.email = 'Email is required';
if (!formData.password) newErrors.password = 'Password is required';
else if (formData.password.length < 6) newErrors.password = 'Password must be at least 6 characters';
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleSubmit = (e) => {
e.preventDefault();
if (validate()) {
console.log('Form submitted:', formData);
}
};
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({ ...prev, [name]: value }));
};
return (
<form onSubmit={handleSubmit}>
<div>
<input
name="email"
value={formData.email}
onChange={handleChange}
placeholder="Email"
/>
{errors.email && <span className="error">{errors.email}</span>}
</div>
<div>
<input
name="password"
type="password"
value={formData.password}
onChange={handleChange}
placeholder="Password"
/>
{errors.password && <span className="error">{errors.password}</span>}
</div>
<button type="submit">Submit</button>
</form>
);
}
function DebouncedSearch() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
useEffect(() => {
const timer = setTimeout(() => {
if (query) {
// 実際にはAPI呼び出しなどを行う
console.log('Searching for:', query);
setResults([`Result 1 for ${query}`, `Result 2 for ${query}`]);
}
}, 500);
return () => clearTimeout(timer);
}, [query]);
return (
<div>
<input type="text" value={query} onChange={(e) => setQuery(e.target.value)} placeholder="Search..." />
<ul>
{results.map((result, index) => (
<li key={index}>{result}</li>
))}
</ul>
</div>
);
}
function FileUpload() {
const [file, setFile] = useState(null);
const handleChange = (e) => {
setFile(e.target.files[0]);
};
return (
<div>
<input type="file" onChange={handleChange} />
{file && (
<div>
<p>Selected file: {file.name}</p>
<p>Size: {file.size} bytes</p>
</div>
)}
</div>
);
}
function ResettableForm() {
const initialState = { name: '', email: '' };
const [formData, setFormData] = useState(initialState);
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({ ...prev, [name]: value }));
};
const handleReset = () => {
setFormData(initialState);
};
return (
<form>
<input name="name" value={formData.name} onChange={handleChange} placeholder="Name" />
<input name="email" value={formData.email} onChange={handleChange} placeholder="Email" />
<button type="button" onClick={handleReset}>
Reset
</button>
</form>
);
}
function useClickOutside(ref, callback) {
useEffect(() => {
const handleClickOutside = (event) => {
if (ref.current && !ref.current.contains(event.target)) {
callback();
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [ref, callback]);
}
// 使用例
function ClickOutsideExample() {
const ref = useRef(null);
const [isOpen, setIsOpen] = useState(false);
useClickOutside(ref, () => setIsOpen(false));
return (
<div>
<button onClick={() => setIsOpen(true)}>Open Menu</button>
{isOpen && (
<div ref={ref} className="menu">
Menu content
</div>
)}
</div>
);
}
const formReducer = (state, action) => {
switch (action.type) {
case 'FIELD_CHANGE':
return {
...state,
[action.field]: action.value,
};
case 'VALIDATE':
return {
...state,
errors: {
email: !state.email ? 'Email is required' : '',
password: !state.password
? 'Password is required'
: state.password.length < 6
? 'Password must be at least 6 characters'
: '',
},
};
case 'RESET':
return initialState;
default:
return state;
}
};
const initialState = {
email: '',
password: '',
errors: {},
};
function FormWithReducer() {
const [state, dispatch] = useReducer(formReducer, initialState);
const handleChange = (e) => {
dispatch({
type: 'FIELD_CHANGE',
field: e.target.name,
value: e.target.value,
});
};
const handleSubmit = (e) => {
e.preventDefault();
dispatch({ type: 'VALIDATE' });
if (!state.errors.email && !state.errors.password) {
console.log('Form submitted:', state);
}
};
return (
<form onSubmit={handleSubmit}>
<div>
<input
name="email"
value={state.email}
onChange={handleChange}
placeholder="Email"
/>
{state.errors.email && <span className="error">{state.errors.email}</span>}
</div>
<div>
<input
name="password"
type="password"
value={state.password}
onChange={handleChange}
placeholder="Password"
/>
{state.errors.password && <span className="error">{state.errors.password}</span>}
</div>
<button type="submit">Submit</button>
</form>
);
}
function FormWithHistory() {
const [formData, setFormData] = useState({ name: '', email: '' });
const [history, setHistory] = useState([{ name: '', email: '' }]);
const [historyIndex, setHistoryIndex] = useState(0);
const handleChange = (e) => {
const { name, value } = e.target;
const newFormData = { ...formData, [name]: value };
setFormData(newFormData);
// 新しい履歴を追加(現在のインデックス以降の履歴は削除)
const newHistory = [...history.slice(0, historyIndex + 1), newFormData];
setHistory(newHistory);
setHistoryIndex(newHistory.length - 1);
};
const undo = () => {
if (historyIndex > 0) {
setHistoryIndex(historyIndex - 1);
setFormData(history[historyIndex - 1]);
}
};
const redo = () => {
if (historyIndex < history.length - 1) {
setHistoryIndex(historyIndex + 1);
setFormData(history[historyIndex + 1]);
}
};
return (
<div>
<form>
<input name="name" value={formData.name} onChange={handleChange} placeholder="Name" />
<input name="email" value={formData.email} onChange={handleChange} placeholder="Email" />
</form>
<div>
<button onClick={undo} disabled={historyIndex === 0}>
Undo
</button>
<button onClick={redo} disabled={historyIndex === history.length - 1}>
Redo
</button>
</div>
<div>
History index: {historyIndex} of {history.length - 1}
</div>
</div>
);
}
function DynamicForm() {
const [fields, setFields] = useState([{ id: 1, value: '' }]);
const addField = () => {
setFields([...fields, { id: Date.now(), value: '' }]);
};
const removeField = (id) => {
setFields(fields.filter(field => field.id !== id));
};
const handleChange = (id, value) => {
setFields(fields.map(field =>
field.id === id ? { ...field, value } : field
));
};
return (
<div>
<button onClick={addField}>Add Field</button>
{fields.map(field => (
<div key={field.id}>
<input
value={field.value}
onChange={(e) => handleChange(field.id, e.target.value)}
/>
<button onClick={() => removeField(field.id)}>Remove</button>
</div>
))}
<pre>{JSON.stringify(fields, null, 2)}</pre>
</div>
);
}
function useMountAnimation(duration = 500) {
const [isMounted, setIsMounted] = useState(false);
const [isAnimating, setIsAnimating] = useState(false);
useEffect(() => {
setIsMounted(true);
setIsAnimating(true);
const timer = setTimeout(() => setIsAnimating(false), duration);
return () => clearTimeout(timer);
}, [duration]);
return { isMounted, isAnimating };
}
// 使用例
function AnimatedBox() {
const { isMounted, isAnimating } = useMountAnimation();
if (!isMounted) return null;
return (
<div className={`box ${isAnimating ? 'fade-in' : ''}`}>
Animated Content
</div>
);
}
// この例では、Incrementボタンをクリックするとcountが更新され、ParentComponentが再レンダリングされます。
// handleClickがuseCallbackでメモ化されていない場合、ExpensiveComponentも再レンダリングされます。
// useCallbackを使用すると、依存配列が変更されない限り同じ関数参照が保持されるため、
// ExpensiveComponentは不必要な再レンダリングを防ぐことができます。
// メモ化が必要なケース: 子コンポーネントにイベントハンドラを渡す場合など
const ExpensiveComponent = React.memo(function({ onClick }) {
console.log('ExpensiveComponent rendered');
return <button onClick={onClick}>Click me</button>;
});
function ParentComponent() {
const [count, setCount] = useState(0);
// メモ化されていないハンドラ - 親が再レンダリングされるたびに新しい関数が作成される
// const handleClick = () => {
// console.log('Button clicked');
// };
// メモ化されたハンドラ
const handleClick = useCallback(() => {
console.log('Button clicked');
}, []);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<ExpensiveComponent onClick={handleClick} />
</div>
);
}