ReactJS Forms & Controlled Components Sinhala Guide | State Management, Validation & Best Practices

ආයුබෝවන් යාලුවනේ! 👋 අද අපි කතා කරන්න යන්නේ ReactJS වලින් web applications හදනකොට අපිට අනිවාර්යයෙන්ම මුණගැහෙන, ඒ වගේම ටිකක් සංකීර්ණ වෙන්න පුළුවන් මාතෘකාවක් ගැන – ඒ තමයි Forms සහ Controlled Components.
ඕනෑම user interaction එකක් තියෙන application එකක form එකක් කියන්නේ හරිම වැදගත් දෙයක්. Registration forms, login forms, contact forms, search bars - මේ හැමදේටම form input handle කරන්න වෙනවා. සාමාන්ය HTML වල වගේ නෙවෙයි, ReactJS වල form input handle කරනකොට අපිට පොඩි වෙනස් ක්රමවේදයක් අනුගමනය කරන්න වෙනවා. මේක හරියට තේරුම් ගත්තොත් ඔයාලගේ applications වල user experience එකත්, code quality එකත් වැඩි කරගන්න පුළුවන්.
මේ tutorial එකෙන් අපි මේ දේවල් ගැන විස්තරාත්මකව බලමු:
- ReactJS වල form input handle කරන මූලික සිද්ධාන්ත.
- Controlled Components සහ Uncontrolled Components අතර වෙනස.
useState
Hook එක පාවිච්චි කරලා form state එක manage කරන හැටි.- Multiple input fields effectively manage කරන හැටි.
- සරල registration form එකක් හදලා, input validation සහ error messages implement කරන හැටි.
- Forms එක්ක වැඩ කරනකොට එන common issues සහ best practices මොනවද කියලා.
එහෙනම්, අපි පටන් ගමු! 🚀
Forms හසුරුවන හැටි: මූලික සංකල්ප
සාමාන්ය HTML වලදී අපි input field එකක ටයිප් කරන දේවල් ඒ input element එක ඇතුලෙම handle වෙනවා. නමුත් React වලදී අපි කැමති අපේ application එකේ state එකට අනුව UI එක update වෙන්න. මේක තමයි React වල reactive nature එකේ මූලිකම හරය. Form inputs වලදීත් අපි මේ මූලික සංකල්පයම පාවිච්චි කරනවා.
React වලදී form input එකක value එක අපේ component එකේ state එකෙන් පාලනය කරනකොට, ඒකට අපි කියනවා Controlled Component එකක් කියලා. මේකෙන් වෙන්නේ input field එකේ value එක හැම වෙලාවෙම React state එකට සමමුහුර්ත (synchronized) වෙන එක.
Controlled Components යනු කුමක්ද?
Controlled Component එකක් කියන්නේ, React state එකකින් තමන්ගේ current value එක පාලනය කරන form input element එකක්. Input field එකක මොනව හරි වෙනසක් වුනොත් (ඒ කියන්නේ user කෙනෙක් මොනවා හරි type කලොත්), ඒ වෙනස React state එකට update වෙනවා. ඊට පස්සේ React state එකේ තියෙන value එක නැවතත් input field එකේ value
prop එකට assign වෙනවා.
වැදගත්කම සහ වාසි:
- Predictable Behavior: Input field එකක value එක හැම වෙලාවෙම state එකෙන් එන නිසා, UI එකේ හැසිරීම predictable වෙනවා.
- Easy Validation: State එකේ value එකට access තියෙන නිසා, real-time input validation implement කිරීම හරිම පහසුයි.
- Immediate Feedback: User input කරන ගමන්ම error messages වගේ දේවල් පෙන්වන්න පුළුවන්.
- Simplified Data Flow: Form data එක එක තැනකින් (state එකෙන්) manage කරන්න පුළුවන්.
සරලවම කිව්වොත්, input field එකේ value
prop එක state එකකට bind කරනවා, සහ onChange
event handler එකක් පාවිච්චි කරලා state එක update කරනවා. මේක තමයි React වල form handling වල රන් රීතිය (golden rule) 👑.
Uncontrolled Components යනු කුමක්ද?
Uncontrolled Component එකක් කියන්නේ, තමන්ගේ form data එක DOM එකෙන් handle කරන input element එකක්. සාමාන්ය HTML input field එකක් වගේමයි. මෙතනදී React state එකකින් value එක පාලනය කරන්නේ නැහැ. Input field එකේ value එක ගන්න ඕන වුනොත්, අපි ref
එකක් පාවිච්චි කරලා DOM එකෙන් ඒ value එක ගන්නවා.
කවදාද පාවිච්චි කරන්නේ?
- හරිම සරල forms වලදී, නැත්නම් validation අවශ්ය නැති තැන් වලදී.
- තවත් library එකක් (third-party library) එක්ක වැඩ කරනකොට, ඒ library එක තමන්ගේ DOM handling කරන්නේ නම්.
නමුත් React වල best practice එක තමයි හැමවිටම Controlled Components පාවිච්චි කරන එක. Uncontrolled components වලට වඩා Controlled components වලින් වැඩි පාලනයක් සහ predictablity එකක් ලැබෙනවා.
useState
Hook එක පාවිච්චි කරලා Form State Manage කරන හැටි
React වලදී state manage කරන්න අපි useState
Hook එක පාවිච්චි කරනවා. Form එකක තියෙන හැම input field එකකටම තමන්ගේම state variable එකක් තියෙන්න පුළුවන්, නැත්නම් form එකේ හැම data එකක්ම තනි object එකක් විදිහටත් state එකක තියාගන්න පුළුවන්.
මුලින්ම අපි බලමු තනි input field එකක් manage කරන හැටි:
import React, { useState } from 'react';
function MyInputField() {
const [inputValue, setInputValue] = useState('');
const handleChange = (event) => {
setInputValue(event.target.value);
};
return (
<div>
<label htmlFor="myInput">ඔයාගේ නම:</label>
<input
type="text"
id="myInput"
value={inputValue} // State එකෙන් value එක පාලනය කරනවා
onChange={handleChange} // Input වෙනස් වෙනකොට state එක update කරනවා
/>
<p>ඔයා type කරපු දේ: {inputValue}</p>
</div>
);
}
export default MyInputField;
මේ උදාහරණයේදී, inputValue
කියන state variable එකෙන් input field එකේ value එක තියාගන්නවා. handleChange
function එක event.target.value
එක පාවිච්චි කරලා state එක update කරනවා. මේක තමයි Controlled Component එකක් වැඩ කරන සරලම විදිහ.
Multiple Input Fields Effectively Manage කරන හැටි
Registration form එකක් වගේ, input fields ගොඩක් තියෙන තැනක, හැම field එකකටම වෙන වෙනම useState
variable එකක් හදන එක ටිකක් අමාරුයි. ඒ වෙනුවට, අපි form data එක තනි object එකක් විදිහට state එකක තියාගන්න පුළුවන්.
මේක කරන්නේ මෙහෙමයි:
import React, { useState } from 'react';
function MyMultiInputForm() {
const [formData, setFormData] = useState({
username: '',
email: '',
password: ''
});
const handleChange = (event) => {
const { name, value } = event.target;
setFormData(prevFormData => ({
...prevFormData, // කලින් තිබ්බ data ටික එහෙම්මම තියාගෙන
[name]: value // අදාල input field එකේ name එකට අදාල value එක update කරනවා
}));
};
const handleSubmit = (event) => {
event.preventDefault(); // Default form submission නවත්වනවා
console.log('Submitted Data:', formData);
alert(`User: ${formData.username}, Email: ${formData.email} registered!`);
// මෙතනින් පුළුවන් data ටික API එකකට යවන්න.
};
return (
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="username">Username:</label>
<input
type="text"
id="username"
name="username" // input field එකේ name prop එක state object එකේ key එකට ගැලපෙන්න ඕන
value={formData.username}
onChange={handleChange}
/>
</div>
<div>
<label htmlFor="email">Email:</label>
<input
type="email"
id="email"
name="email"
value={formData.email}
onChange={handleChange}
/>
</div>
<div>
<label htmlFor="password">Password:</label>
<input
type="password"
id="password"
name="password"
value={formData.password}
onChange={handleChange}
/>
</div>
<button type="submit">Register</button>
</form>
);
}
export default MyMultiInputForm;
මෙහිදී handleChange
function එකේ [name]: value
කියන syntax එකෙන් dynamic object keys හදලා අදාල input field එකේ state එක විතරක් update කරනවා. මේක හරිම powerful සහ clean method එකක්.
Registration Form එකක් හදමු: Practical Example
දැන් අපි බලමු, කලින් කතා කරපු Concepts ටික පාවිච්චි කරලා, සරල registration form එකක් හදන්නේ කොහොමද කියලා. මේ form එකේ name, email, password fields ටිකක් තියෙනවා. ඒ වගේම input validation සහ error messages පෙන්වන හැටිත් අපි බලමු.
මුලින්ම, අපි RegistrationForm.js
කියලා අලුත් component file එකක් හදාගමු.
import React, { useState } from 'react';
function RegistrationForm() {
const [formData, setFormData] = useState({
fullName: '',
email: '',
password: '',
});
const [errors, setErrors] = useState({}); // Validation errors තියාගන්න state එකක්
const [isSubmitted, setIsSubmitted] = useState(false); // Form එක submit කරාද කියලා දැනගන්න
const handleChange = (event) => {
const { name, value } = event.target;
setFormData(prevFormData => ({
...prevFormData,
[name]: value
}));
// Input එක වෙනස් වෙනකොට validation error එක clear කරන්න පුළුවන්
if (errors[name]) {
setErrors(prevErrors => ({
...prevErrors,
[name]: null
}));
}
};
const validateForm = () => {
let newErrors = {};
if (!formData.fullName.trim()) {
newErrors.fullName = 'Full Name එක අනිවාර්යයි!';
}
if (!formData.email.trim()) {
newErrors.email = 'Email එක අනිවාර්යයි!';
} else if (!/^[\w-.]+@([\w-]+\.)+[\w-]{2,4}$/.test(formData.email)) {
newErrors.email = 'වලංගු Email ලිපිනයක් ඇතුලත් කරන්න.';
}
if (!formData.password) {
newErrors.password = 'Password එක අනිවාර්යයි!';
} else if (formData.password.length < 6) {
newErrors.password = 'Password එක අකුරු 6කට වඩා දිග විය යුතුයි.';
}
return newErrors;
};
const handleSubmit = (event) => {
event.preventDefault();
setIsSubmitted(true);
const validationErrors = validateForm();
setErrors(validationErrors);
if (Object.keys(validationErrors).length === 0) {
// Validation සාර්ථකයි, දැන් data ටික submit කරන්න පුළුවන්
console.log('Registration Data:', formData);
alert('Registration Successful! ✅ Check console for data.');
// Form එක clear කරන්න පුළුවන්
setFormData({
fullName: '',
email: '',
password: '',
});
setIsSubmitted(false);
} else {
console.log('Form has errors:', validationErrors);
}
};
return (
<div style={{ maxWidth: '400px', margin: '50px auto', padding: '20px', border: '1px solid #ccc', borderRadius: '8px', boxShadow: '0 2px 4px rgba(0,0,0,0.1)' }}>
<h2 style={{ textAlign: 'center', color: '#333' }}>Register Now!</h2>
<form onSubmit={handleSubmit} noValidate> {/* noValidate HTML5 validation නවත්වනවා */}
<div style={{ marginBottom: '15px' }}>
<label htmlFor="fullName" style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold' }}>Full Name:</label>
<input
type="text"
id="fullName"
name="fullName"
value={formData.fullName}
onChange={handleChange}
style={{ width: '100%', padding: '10px', border: errors.fullName ? '1px solid red' : '1px solid #ddd', borderRadius: '4px', boxSizing: 'border-box' }}
/>
{errors.fullName && <p style={{ color: 'red', fontSize: '0.9em', marginTop: '5px' }}>{errors.fullName}</p>}
</div>
<div style={{ marginBottom: '15px' }}>
<label htmlFor="email" style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold' }}>Email:</label>
<input
type="email"
id="email"
name="email"
value={formData.email}
onChange={handleChange}
style={{ width: '100%', padding: '10px', border: errors.email ? '1px solid red' : '1px solid #ddd', borderRadius: '4px', boxSizing: 'border-box' }}
/>
{errors.email && <p style={{ color: 'red', fontSize: '0.9em', marginTop: '5px' }}>{errors.email}</p>}
</div>
<div style={{ marginBottom: '20px' }}>
<label htmlFor="password" style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold' }}>Password:</label>
<input
type="password"
id="password"
name="password"
value={formData.password}
onChange={handleChange}
style={{ width: '100%', padding: '10px', border: errors.password ? '1px solid red' : '1px solid #ddd', borderRadius: '4px', boxSizing: 'border-box' }}
/>
{errors.password && <p style={{ color: 'red', fontSize: '0.9em', marginTop: '5px' }}>{errors.password}</p>}
</div>
<button type="submit" style={{ width: '100%', padding: '10px 15px', backgroundColor: '#007bff', color: 'white', border: 'none', borderRadius: '4px', fontSize: '1em', cursor: 'pointer' }}>Register</button>
</form>
</div>
);
}
export default RegistrationForm;
මේ RegistrationForm
component එක App.js
එකට import කරලා run කරලා බලන්න.
// App.js
import React from 'react';
import RegistrationForm from './RegistrationForm'; // ඔබගේ component file එකට අදාල path එක දෙන්න
function App() {
return (
<div className="App">
<RegistrationForm />
</div>
);
}
export default App;
මේ code එකේ අපි formData
object එකෙන් form එකේ inputs වල values තියාගන්නවා. errors
object එකෙන් validation errors තියාගන්නවා. validateForm
කියන function එකෙන් form එක submit කරනකොට validation checks කරනවා. Errors තියෙනවා නම්, ඒවා errors
state එකට දාලා අදාල input field එක යටින් පෙන්වනවා. Errors නැත්නම්, registration data ටික console එකේ log කරනවා.
Common Pitfalls සහ Best Practices
React forms එක්ක වැඩ කරනකොට මුලින් මුලින් පොඩි පොඩි වැරදීම් වෙන්න පුළුවන්. ඒ වගේම හොඳම විදිහට forms handle කරන්න best practices ටිකක් දැනගෙන ඉන්න එකත් හරිම වැදගත්.
1. Inputs not updating when typing (forgetting onChange
)
මේක තමයි Controlled Components එක්ක වැඩ කරනකොට ගොඩක් අයට මුලින්ම වෙන පොදු වැරැද්ද. ඔයා input field එකකට value
prop එකක් දුන්නොත්, ඒ input field එක Controlled Component එකක් බවට පත්වෙනවා. එතකොට ඒකේ value එක state එකෙන් පාලනය වෙන්න ඕන. හැබැයි ඔයා onChange
event handler එකක් දීලා state එක update කළේ නැත්නම්, input field එකේ value එක වෙනස් වෙන්නේ නැහැ. මොකද React, state එකේ තියෙන value එක නැවත input එකට දාන නිසා.
Example of the problem:
<input type="text" value={inputValue} /> {/* onChange නැති නිසා input එකේ ටයිප් කරන්න බැහැ */}
Solution: හැමවිටම onChange
prop එකක් දීලා state එක update කරන්න.
<input type="text" value={inputValue} onChange={handleChange} />
2. State updates not reflecting immediately
ඔබ setInputValue
හෝ setFormData
වැනි state update function එකක් කැඳවූ විට, state value එක ක්ෂණිකව update වන්නේ නැහැ. React state updates asynchronous (අසමමුහුර්ත) ලෙස ක්රියාත්මක වෙනවා. ඒ කියන්නේ, ඔබ setFormData
කැඳවූ වහාම ඊට යටින් console.log(formData)
දැමුවොත්, ඔබට පෙන්වන්නේ කලින් state value එක මිස අලුත් එක නොවේ.
Solution: නව state value එක අවශ්ය නම්, useEffect
Hook එක පාවිච්චි කරන්න, නැත්නම් state update function එකේ callback එකක් පාවිච්චි කරන්න (useState
වලට callback support එකක් නැති නිසා, useEffect
තමයි මේකට හොඳම විදිහ).
import React, { useState, useEffect } from 'react';
function MyComponent() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
console.log('Immediately after setCount:', count); // මෙය කලින් count value එක පෙන්වයි
};
// count වෙනස් වුනහම විතරක් මේක run වෙනවා
useEffect(() => {
console.log('Count updated to:', count); // මෙය අලුත් count value එක පෙන්වයි
}, [count]);
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
}
3. Best Practice: හැමවිටම Controlled Components පාවිච්චි කරන්න
කලින් කිව්වා වගේ, විශේෂ හේතුවක් නොමැතිනම් හැමවිටම Controlled Components පාවිච්චි කරන්න. මේකෙන් ඔයාගේ application එක වඩාත් predictable, testable, සහ maintainable වෙනවා. Validation, dynamic behavior (උදාහරණයක් ලෙස, එක් input එකක value එක අනුව තවත් input එකක් enable/disable කිරීම) වගේ දේවල් හසුරුවන්න හරිම පහසුයි.
4. Best Practice: Lifting State Up
සමහර වෙලාවට, form data එක එකම component එකකට විතරක් අවශ්ය වෙන්නේ නැහැ. සමහරවිට form එකේ data එක parent component එකකට නැත්නම් වෙනත් child component එකකට අවශ්ය වෙන්න පුළුවන්. මේ වගේ අවස්ථාවලදී, අපි form එකේ state එක parent component එකට lift up කරනවා.
සරලවම කිව්වොත්, data එක අවශ්ය වන සියලුම components වලට පොදු වන ආසන්නතම parent component එකේ state එක තබාගෙන, prop drilling (props හරහා data පහලට යැවීම) මගින් child components වලට data සහ state update functions ලබා දෙනවා.
// ParentComponent.js
import React, { useState } from 'react';
import ChildForm from './ChildForm';
function ParentComponent() {
const [formData, setFormData] = useState({
name: '',
age: ''
});
const handleFormChange = (newFormData) => {
setFormData(newFormData);
};
const handleSubmitAll = () => {
console.log('Final Data from Parent:', formData);
// Data එක Server එකට යවන්න පුළුවන්
};
return (
<div>
<h2>Parent Component</h2>
<ChildForm formData={formData} onFormChange={handleFormChange} />
<button onClick={handleSubmitAll}>Submit All</button>
<p>Current Name: {formData.name}, Age: {formData.age}</p>
</div>
);
}
export default ParentComponent;
// ChildForm.js
import React from 'react';
function ChildForm({ formData, onFormChange }) {
const handleChange = (event) => {
const { name, value } = event.target;
onFormChange({
...formData,
[name]: value
});
};
return (
<div>
<h3>Child Form</h3>
<label>Name:</label>
<input
type="text"
name="name"
value={formData.name}
onChange={handleChange}
/>
<label>Age:</label>
<input
type="text"
name="age"
value={formData.age}
onChange={handleChange}
/>
</div>
);
}
export default ChildForm;
මේ උදාහරණයේදී, formData
state එක ParentComponent
එකේ තියෙනවා. ChildForm
එකට ඒ data එක props හරහා ලැබෙනවා. ChildForm
එකේ input එකක් වෙනස් වෙනකොට, onFormChange
prop එක හරහා ParentComponent
එකේ state එක update කරනවා. මේක තමයි Lifting State Up කියන්නේ.
අවසන් වචනය
ReactJS එක්ක Forms සහ Controlled Components කියන්නේ ටිකක් මුලින්ම අවබෝධ කරගන්න අමාරු වුනත්, හරිම වැදගත් මාතෘකාවක්. ඔබ මේ සංකල්ප හරියට තේරුම් ගත්තොත්, ඔබේ React applications වල forms හසුරුවන එක හරිම පහසු වෙයි. මතක තියාගන්න:
- හැමවිටම Controlled Components පාවිච්චි කරන්න.
useState
Hook එකෙන් form state එක manage කරන්න.event.target.name
සහevent.target.value
පාවිච්චි කරලා multiple inputs handle කරන්න.- Validation implement කරලා user ට හොඳ feedback එකක් දෙන්න.
onChange
prop එක අමතක කරන්න එපා.- අවශ්ය නම් Lifting State Up සංකල්පය පාවිච්චි කරන්න.
දැන් ඔයාලට පුළුවන් මේ දැනුම පාවිච්චි කරලා ඔයාලගේම forms හදන්න පටන් ගන්න. මේ registration form එක modify කරලා බලන්න, අලුත් validation rules දාන්න, නැත්නම් වෙනත් input types (checkboxes, radio buttons, selects) කොහොමද handle කරන්නේ කියලා හොයලා බලන්න. 🧑💻
මේ tutorial එක ගැන ඔයාලගේ අදහස්, ප්රශ්න, නැත්නම් ඔයාලට මුණගැහුණු වෙනත් forms related issues comment section එකේ දාගෙන යන්න. අපි බලමු ඒවාට උත්තර දෙන්න! Happy coding! 💻