State
מהו State?
State הוא נתון פנימי של קומפוננטה שיכול להשתנות לאורך זמן - למשל ספירה שעולה, טקסט שהמשתמש מקליד או חלון שנפתח ונסגר. זהו ההבדל המהותי מ-props: את ה-State הקומפוננטה מנהלת בעצמה. הכוח שלו טמון בכך שבכל פעם שה-State משתנה, React מרנדרת את הקומפוננטה מחדש ומעדכנת את המסך אוטומטית.
import { useState } from 'react';
function Counter() {
// הגדרת state עם ערך התחלתי 0
const [count, setCount] = useState(0);
return (
<div>
<p>ספירה: {count}</p>
<button onClick={() => setCount(count + 1)}>
הוסף 1
</button>
</div>
);
}
useState Hook
התחביר
const [value, setValue] = useState(initialValue);
// ↑ ↑ ↑
// ערך פונקציית ערך התחלתי
// נוכחי עדכון
דוגמאות
// מספר
const [count, setCount] = useState(0);
// מחרוזת
const [name, setName] = useState("");
// בוליאני
const [isOpen, setIsOpen] = useState(false);
// מערך
const [items, setItems] = useState([]);
// אובייקט
const [user, setUser] = useState({ name: "", age: 0 });
עדכון State
עדכון ישיר
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(5)}>הגדר ל-5</button>
<button onClick={() => setCount(count + 1)}>+1</button>
<button onClick={() => setCount(count - 1)}>-1</button>
</div>
);
}
עדכון מבוסס ערך קודם (מומלץ!)
function Counter() {
const [count, setCount] = useState(0);
// ✅ מומלץ - שימוש בפונקציית callback
const increment = () => setCount(prev => prev + 1);
const decrement = () => setCount(prev => prev - 1);
const double = () => setCount(prev => prev * 2);
return (
<div>
<p>{count}</p>
<button onClick={increment}>+1</button>
<button onClick={decrement}>-1</button>
<button onClick={double}>×2</button>
</div>
);
}
State עם טפסים
function LoginForm() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
console.log("Email:", email);
console.log("Password:", password);
};
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="אימייל"
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="סיסמה"
/>
<button type="submit">התחבר</button>
</form>
);
}
State עם אובייקטים
function UserForm() {
const [user, setUser] = useState({
name: "",
email: "",
age: 0
});
// ❌ שגוי - לא לשנות ישירות
// user.name = "דני";
// ✅ נכון - יצירת אובייקט חדש
const updateName = (name) => {
setUser({ ...user, name: name });
};
// ✅ או בגרסה מקוצרת
const updateEmail = (email) => {
setUser(prev => ({ ...prev, email }));
};
return (
<div>
<input
value={user.name}
onChange={(e) => updateName(e.target.value)}
/>
<input
value={user.email}
onChange={(e) => updateEmail(e.target.value)}
/>
</div>
);
}
State עם מערכים
function TodoList() {
const [todos, setTodos] = useState([
{ id: 1, text: "לקנות חלב", done: false },
{ id: 2, text: "לשלם חשבונות", done: false }
]);
// הוספת פריט
const addTodo = (text) => {
setTodos(prev => [...prev, {
id: Date.now(),
text,
done: false
}]);
};
// מחיקת פריט
const deleteTodo = (id) => {
setTodos(prev => prev.filter(todo => todo.id !== id));
};
// עדכון פריט
const toggleTodo = (id) => {
setTodos(prev => prev.map(todo =>
todo.id === id
? { ...todo, done: !todo.done }
: todo
));
};
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>
<span
style={{ textDecoration: todo.done ? 'line-through' : 'none' }}
onClick={() => toggleTodo(todo.id)}
>
{todo.text}
</span>
<button onClick={() => deleteTodo(todo.id)}>❌</button>
</li>
))}
</ul>
);
}
State vs Props
| תכונה | Props | State |
|---|---|---|
| מקור | מגיע מההורה | פנימי לקומפוננטה |
| שינוי | לא ניתן לשנות | ניתן לשנות עם setter |
| מטרה | העברת נתונים | ניהול נתונים משתנים |
| רינדור מחדש | כשההורה מתרנדר | כש-State משתנה |
// Props - נתונים מבחוץ (read-only)
function Greeting({ name }) {
return <h1>שלום {name}!</h1>;
}
// State - נתונים פנימיים (mutable)
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}
// שילוב: State בהורה, Props לילד
function App() {
const [name, setName] = useState("דני");
return (
<div>
<input value={name} onChange={(e) => setName(e.target.value)} />
<Greeting name={name} /> {/* State הופך ל-Props */}
</div>
);
}
כללים חשובים
1. אל תשנו State ישירות
// ❌ שגוי
count = count + 1;
user.name = "דני";
todos.push({ id: 1, text: "חדש" });
// ✅ נכון
setCount(count + 1);
setUser({ ...user, name: "דני" });
setTodos([...todos, { id: 1, text: "חדש" }]);
2. State הוא אסינכרוני
קריאה ל-setter אינה מעדכנת את הערך באופן מיידי, אלא מתזמנת רינדור מחדש. לכן מיד אחרי הקריאה המשתנה עדיין מחזיק את הערך הישן. אם צריך לחשב על בסיס הערך העדכני, משתמשים בגרסת ה-callback (prev => ...):
function Example() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
console.log(count); // עדיין הערך הישן!
};
}
3. מספר useState בקומפוננטה אחת
function Form() {
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [age, setAge] = useState(0);
const [isValid, setIsValid] = useState(false);
// ...
}
סיכום
| פעולה | קוד |
|---|---|
| הגדרת State | const [x, setX] = useState(initial) |
| עדכון ישיר | setX(newValue) |
| עדכון מבוסס קודם | setX(prev => prev + 1) |
| עדכון אובייקט | setX(prev => ({...prev, key: val})) |
| הוספה למערך | setX(prev => [...prev, newItem]) |
| מחיקה ממערך | setX(prev => prev.filter(...)) |
| עדכון במערך | setX(prev => prev.map(...)) |
בדקו את עצמכם
נסו לענות לבד לפני שאתם פותחים את התשובה.
-
למה לא כותבים
count = count + 1ישירות?הצגת התשובה
תשובה ד. רק קריאה ל-setCount מודיעה ל-React לרנדר מחדש.
-
איך מעדכנים state לפי הערך הקודם בבטחה?
הצגת התשובה
תשובה ג. הצורה הפונקציונלית תמיד מקבלת את הערך העדכני, גם בכמה עדכונים רצופים.
-
איך מוסיפים איבר למערך ב-state?
הצגת התשובה
תשובה ב. יוצרים מערך חדש. שינוי המערך הקיים לא יגרום לרינדור.