
React演習問題(JSX・コンポーネント・Props・State)
初級問題(9問) JSXの書き方 <div class="container"> <h1 id="title&q […]
Reactコンポーネントの核心概念であるProps(プロパティ)とState(状態)について、初心者向けに徹底解説します。これらの概念を理解することで、動的でインタラクティブなアプリケーションを構築できるようになります。
Props(プロパティ)は親コンポーネントから子コンポーネントにデータを渡すための仕組みです。以下の特徴があります:
// 親コンポーネント
function ParentComponent() {
return <ChildComponent name="山田太郎" age={25} />;
}
// 子コンポーネント
function ChildComponent(props) {
return (
<div>
<p>名前: {props.name}</p>
<p>年齢: {props.age}</p>
</div>
);
}
Stateはコンポーネント内部で管理される状態データです。以下の特徴があります:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0); // Stateの宣言
return (
<div>
<p>現在のカウント: {count}</p>
<button onClick={() => setCount(count + 1)>増やす</button>
</div>
);
}
// 方法1: propsオブジェクトを使用
function Welcome(props) {
return <h1>こんにちは、{props.name}さん!</h1>;
}
// 方法2: 分割代入を使用(推奨)
function Welcome({ name }) {
return <h1>こんにちは、{name}さん!</h1>;
}
// 使用例
<Welcome name="佐藤花子" />
class Welcome extends React.Component {
render() {
return <h1>こんにちは、{this.props.name}さん!</h1>;
}
}
// 使用例
<Welcome name="田中一郎" />
Propsが渡されなかった場合のデフォルト値を設定できます。
function Welcome({ name }) {
return <h1>こんにちは、{name}さん!</h1>;
}
Welcome.defaultProps = {
name: 'ゲスト'
};
// nameが未指定の場合
<Welcome /> // "こんにちは、ゲストさん!"と表示
コンポーネントタグの間に記述した内容はchildren
として渡されます。
function Card({ children }) {
return <div className="card">{children}</div>;
}
// 使用例
<Card>
<h2>タイトル</h2>
<p>コンテンツ</p>
</Card>
大規模アプリケーションではPropsの型をチェックすると良いでしょう。
import PropTypes from 'prop-types';
function UserProfile({ name, age, hobbies }) {
// ...
}
UserProfile.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number,
hobbies: PropTypes.arrayOf(PropTypes.string)
};
UserProfile.defaultProps = {
age: 20,
hobbies: []
};
React 16.8で導入されたHooks APIを使用します。
import { useState } from 'react';
function Counter() {
// count: 現在の状態値
// setCount: 状態を更新する関数
const [count, setCount] = useState(0); // 初期値0
return (
<div>
<p>カウント: {count}</p>
<button onClick={() => setCount(count + 1)>増やす</button>
<button onClick={() => setCount(count - 1)>減らす</button>
<button onClick={() => setCount(0)>リセット</button>
</div>
);
}
function Form() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [age, setAge] = useState(20);
return (
<form>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="名前"
/>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="メールアドレス"
/>
<input
type="number"
value={age}
onChange={(e) => setAge(Number(e.target.value))}
placeholder="年齢"
/>
</form>
);
}
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
increment = () => {
this.setState({ count: this.state.count + 1 });
};
decrement = () => {
this.setState({ count: this.state.count - 1 });
};
reset = () => {
this.setState({ count: 0 });
};
render() {
return (
<div>
<p>カウント: {this.state.count}</p>
<button onClick={this.increment}>増やす</button>
<button onClick={this.decrement}>減らす</button>
<button onClick={this.reset}>リセット</button>
</div>
);
}
}
// 誤り
count = count + 1;
// 正しい
setCount(count + 1);
setCount(prevCount => prevCount + 1);
// 誤り(再レンダリングが発生しない)
this.state.count = this.state.count + 1;
// 正しい
this.setState({ count: this.state.count + 1 });
// 誤り(this.state.countが最新でない可能性)
this.setState({ count: this.state.count + 1 });
// 正しい
this.setState((prevState) => ({
count: prevState.count + 1
}));
function UserForm() {
const [user, setUser] = useState({
name: '',
email: '',
age: 20
});
const handleChange = (e) => {
const { name, value } = e.target;
setUser(prevUser => ({
...prevUser, // 既存のstateを展開
[name]: value // 変更されたプロパティを更新
}));
};
return (
<form>
<input
name="name"
value={user.name}
onChange={handleChange}
placeholder="名前"
/>
<input
name="email"
value={user.email}
onChange={handleChange}
placeholder="メールアドレス"
/>
<input
name="age"
type="number"
value={user.age}
onChange={handleChange}
placeholder="年齢"
/>
</form>
);
}
Reactのデータフローは「単方向(unidirectional)」です:
// 親コンポーネント
function ParentComponent() {
const [todos, setTodos] = useState([
{ id: 1, text: 'Reactを学ぶ', completed: false },
{ id: 2, text: 'プロジェクトを作成', completed: false },
{ id: 3, text: 'デプロイする', completed: false }
]);
const toggleTodo = (id) => {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
));
};
return (
<div>
<h1>Todoリスト</h1>
<TodoList todos={todos} onToggleTodo={toggleTodo} />
</div>
);
}
// 子コンポーネント
function TodoList({ todos, onToggleTodo }) {
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => onToggleTodo(todo.id)}
/>
<span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
{todo.text}
</span>
</li>
))}
</ul>
);
}
function App() {
const [user, setUser] = useState({
name: '山田太郎',
preferences: {
theme: 'dark',
fontSize: 16
}
});
return (
<div className={`app ${user.preferences.theme}`}>
<Header user={user} />
<MainContent user={user} />
<Footer />
</div>
);
}
function Header({ user }) {
return (
<header>
<h1>ようこそ、{user.name}さん</h1>
<UserSettings user={user} />
</header>
);
}
function UserSettings({ user }) {
return (
<div className="settings">
<p>現在のテーマ: {user.preferences.theme}</p>
<p>フォントサイズ: {user.preferences.fontSize}px</p>
</div>
);
}
// 絶対にしないでください
function ChildComponent(props) {
props.name = '変更'; // エラーが発生
// ...
}
// 避けるべき
const [user, setUser] = useState({
profile: {
personal: {
name: '',
address: {
city: '',
street: ''
}
}
}
});
// 平坦化を検討
const [name, setName] = useState('');
const [city, setCity] = useState('');
const [street, setStreet] = useState('');
function ChildComponent(props) {
console.log('Received props:', props); // コンソールで確認
return <div>...</div>;
}
function MyComponent() {
const [state, setState] = useState(/* ... */);
useEffect(() => {
console.log('State changed:', state);
}, [state]); // stateが変更されるたびにログ出力
// ...
}
PropsとStateを適切に使い分けることで、Reactアプリケーションのデータフローを明確に保つことができます。次に学ぶイベント処理やライフサイクルメソッドと組み合わせることで、さらにインタラクティブなコンポーネントを作成できるようになります。