build() vs render()
ใน React component function (หรือ render() ใน class components) ถูกเรียกทุกครั้งที่ state หรือ props เปลี่ยน function นั้นต้องเป็น pure function: ถ้า state และ props เหมือนกัน ก็ต้อง return JSX เหมือนกัน โดยไม่มี side effects ใน body
build() method ของ Flutter ปฏิบัติตาม contract เดียวกันทุกประการ framework เรียก build() ทุกครั้งที่ rebuild ถูก trigger โดย setState, parent rebuild, หรือการเปลี่ยนแปลง InheritedWidget และ build() ต้อง pure และราคาถูก: ห้าม network calls, ห้าม Future.wait, ห้าม computation หนัก — เป็นแค่การบรรยาย UI
กฎจริงของทั้งสอง framework นั้นเหมือนกัน: describe, don’t do (บรรยาย อย่าทำ)
การเปรียบเทียบแบบ side-by-side
หัวข้อที่มีชื่อว่า “การเปรียบเทียบแบบ side-by-side”// React — component function คือ "render"function UserCard({ userId }) { const [user, setUser] = useState(null);
// Side effects ไปอยู่ใน useEffect ไม่ใช่ใน render body useEffect(() => { fetchUser(userId).then(setUser); }, [userId]);
// render body: pure description เท่านั้น if (!user) return <p>Loading...</p>; return ( <div className="card"> <h2>{user.name}</h2> <p>{user.email}</p> </div> );}class UserCard extends StatefulWidget { final String userId; const UserCard({super.key, required this.userId});
@override State<UserCard> createState() => _UserCardState();}
class _UserCardState extends State<UserCard> { User? _user;
// Side effects ไปอยู่ใน initState / didUpdateWidget ไม่ใช่ใน build() @override void initState() { super.initState(); _loadUser(); }
Future<void> _loadUser() async { final u = await fetchUser(widget.userId); if (mounted) setState(() => _user = u); }
// build(): pure description เท่านั้น @override Widget build(BuildContext context) { if (_user == null) return const CircularProgressIndicator(); return Card( child: Column(children: [ Text(_user!.name, style: const TextStyle(fontWeight: FontWeight.bold)), Text(_user!.email), ]), ); }}ตัวอย่างที่รันได้
หัวข้อที่มีชื่อว่า “ตัวอย่างที่รันได้”import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal)),
home: const Scaffold(body: Center(child: RebuildDemo())),
);
}
}
// Widget นี้นับว่า build() ถูกเรียกกี่ครั้ง —
// แสดงให้เห็นว่า build() ราคาถูกและทำงานบ่อย
class RebuildDemo extends StatefulWidget {
const RebuildDemo({super.key});
@override
State<RebuildDemo> createState() => _RebuildDemoState();
}
class _RebuildDemoState extends State<RebuildDemo> {
int _counter = 0;
int _buildCount = 0; // เพิ่มใน build เพื่อแสดงความถี่
@override
Widget build(BuildContext context) {
// ติดตาม rebuild count — นี่คือ "side effect" ที่ยอมรับได้เพียงอย่างเดียว
// ใน build(): การเพิ่ม field แบบ synchronous เพื่อ demo
_buildCount++;
return Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Counter: $_counter',
style: const TextStyle(fontSize: 32, fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
Text('build() called: $_buildCount times',
style: const TextStyle(color: Colors.grey)),
const SizedBox(height: 24),
ElevatedButton(
onPressed: () => setState(() => _counter++),
child: const Text('Increment (triggers rebuild)'),
),
],
),
);
}
}