下面说明三种组件包装方式、数据如何流动,以及
function与箭头函数的区别。
1. 三种写法,三种职责
项目 Home 页里同时出现三种写法,它们解决的是不同问题:
// observer — 订阅 MobX,数据变了要更新
const HomeContent = observer(function HomeContent({ navigation }) {
const deviceState = useDeviceState()
return <HomeView deviceTitle={...} statusSubtitle={...} />
})
// 普通组件 — 只吃 props,负责画 UI
const HomeView = ({ deviceTitle, statusSubtitle, ... }) => {
return ( ... )
}
// memo — props 没变时,跳过重复渲染
const NavBar = memo(({ title, subtitle, onBack, onMore }) => {
return ( ... )
})
| 写法 | 解决什么问题 | 典型场景 |
|---|---|---|
observer |
MobX 变了,组件要重新 render | 读 useDeviceState() 的页面/容器 |
| 普通组件 | 只根据 props 渲染 |
HomeView 等展示层 |
memo |
父 render 了,但 props 没变 → 不重绘 |
NavBar、FunctionCard 等叶子组件 |
决策流程:
组件里会读 MobX store 吗?
├─ 是 → 用 observer
└─ 否 → 只是展示 props?
├─ 是,且父组件常 render、props 常不变 → 用 memo
└─ 否 → 普通函数组件即可
2. 数据怎么流动:为什么 HomeContent 变了,HomeView 也会变
React 和 MobX 是两套更新机制。useState 变了 React 知道;MobX store 变了,React 默认不知道。所以谁读了 MobX,谁就要包 observer。
HomeContent 读了 store,必须包 observer;HomeView 不读 store,只收 props,不需要 observer,但照样会更新:
MobX store 变化
↓
HomeContent(observer)重新 render
↓
算出新的 deviceTitle、statusSubtitle 等
↓
<HomeView deviceTitle={新值} ... /> ← props 变了
↓
HomeView 重新 render ← 父 render,子跟着 render
可以把 HomeContent 想成「看仓库的人」,HomeView 想成「只显示上级传来数字的板子」。仓库变了 → 看仓库的人更新 → 传新 props → 板子内容变了。板子不用自己订阅 MobX。
observer 管的是「MobX 变了,谁该重新 render」,不是「子组件也要包 observer 才会更新」。只有组件自己读 store 时,才需要在它上面包 observer。
和 memo 对比:
-
HomeContent变 →HomeView会变(父传新 props) -
HomeContent变 →NavBar可能不变(若title、subtitle未变,memo会拦住)
注意:若每次 render 都传新函数,如 onMore={() => navigation.navigate('Settings')},NavBar 的 memo 会失效,需要用 useCallback 稳定引用。
Home 页完整结构:
MobX deviceState
↓
HomeContent(observer)— 读数据、算逻辑
↓ props
HomeView(普通组件)— 组装布局
↓ props
NavBar / FunctionCard(memo)— 纯 UI,可跳过无效 render
3. 函数形式:function HomeContent 与 ({ ... }) => 的区别
这两种写法都是合法的 React 函数组件,功能上等价,区别在 JavaScript 语法和习惯,与 observer / memo 无关。
命名函数:
const HomeContent = observer(function HomeContent({ navigation }) {
// ...
})
箭头函数:
const HomeView = ({
navigation,
deviceTitle = 'OZ601 Ultra',
statusSubtitle = 'Standby',
isOffline = false,
onMenuPress = () => {},
}) => {
// ...
}
| 点 | function 组件名() |
const 组件名 = () => |
|---|---|---|
| 函数名 | 直接写在 function 后 |
名字来自变量 |
| DevTools | 显示 HomeContent,调试清晰 |
有时显示 Observer 或匿名 |
this |
有自己的 this
|
继承外层(函数组件里基本不用) |
项目里常见组合:observer 包容器时用 命名函数(DevTools 好看);展示组件、小组件用 箭头函数(更短)。团队统一用一种也可以,不是硬性规定。
4. 参数写法:解构与默认值
HomeView 参数里的大括号不是特殊语法,而是 props 解构 + 默认值:
const HomeView = ({
navigation,
deviceTitle = 'OZ601 Ultra', // 没传时用默认值
statusSubtitle = 'Standby',
onMenuPress = () => {},
}) => { ... }
等价于:
function HomeView(props) {
const navigation = props.navigation
const deviceTitle = props.deviceTitle ?? 'OZ601 Ultra'
// ...
}
解构写法更简洁,箭头函数和 function 都可以这样写:
function HomeView({ deviceTitle = 'xxx' }) { ... } // 完全等价
const HomeView = ({ deviceTitle = 'xxx' }) => { ... }
5. 团队实践与一句话总结
推荐分层:
-
index.js:observer容器,读 store、算数据 -
XxxView.js:普通展示组件,只收 props -
components/:可复用 UI,按需memo
三句话记住:
- 谁读 MobX,谁包
observer - 只吃 props 的组件,父 render 就会跟着变,不必包
observer function和=>是写法差异;{ navigation, title = '...' }是解构 + 默认值,两种函数形式都能用