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())),
);
}
}
// This widget counts how many times build() is called —
// demonstrating that build() is cheap and runs frequently.
class RebuildDemo extends StatefulWidget {
const RebuildDemo({super.key});
@override
State<RebuildDemo> createState() => _RebuildDemoState();
}
class _RebuildDemoState extends State<RebuildDemo> {
int _counter = 0;
int _buildCount = 0; // incremented inside build to show frequency
@override
Widget build(BuildContext context) {
// Tracking rebuild count — this is the ONLY acceptable "side effect"
// in build(): a synchronous field increment for demo purposes.
_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)'),
),
],
),
);
}
}