React 一面:组件通信、列表 key 与表单处理
很多公司前端一面会有几道 React 相关的问题,这一篇挑三个高频主题:组件通信、列表 key 以及表单处理,配上几道典型题的参考答案。
组件通信:从 props 到 Context
常见的组件通信方式:
- 父 → 子:通过 props 传递数据和回调;
- 子 → 父:通过 props 传下来的回调触发(例如
onChange); - 兄弟组件:
- 通过共同的父组件“状态提升”;
- 或通过 Context/状态管理库共享状态。
一面里常见的问法是:
- “React 组件之间有哪些通信方式?”
- “什么时候应该用 Context,而不是一层层传 props?”
参考讲法:
- 优先考虑状态提升:当多个子组件需要共享数据时,把状态提到共同父级,通过 props 分发;
- 如果层级太深且 props drilling 明显影响可维护性,可以用 Context 做一层“全局订阅”;
- 对于更复杂的场景(多模块共享、异步流、持久化),再考虑 Redux/Zustand 等状态管理库。
列表渲染与 key:为什么一定要 key?
在 React 里,使用 Array.map 渲染列表时,需要给每个元素一个稳定的 key:
1{items.map((item) => (
2 <Item key={item.id} data={item} />
3))}
一面常见考点:
- 为什么不推荐用数组索引作为 key?
- key 的作用是什么,错用 key 会带来什么问题?
参考回答要点:
- key 用来帮助 React 判断“哪些元素是新增/删除/复用”,从而最小化 DOM 操作;
- 如果用索引作为 key,在列表前面插入/删除元素时,后面的组件会被错误复用,可能导致:
- 输入框内容错位;
- 组件内部状态混乱;
- 动画/过渡异常。
可以用一个简单例子说明“输入框绑定列表项时错用索引 key 会导致输入错位”,面试官会更有画面感。
表单处理:受控组件 vs 非受控组件
React 中表单通常有两种处理方式:
受控组件(Controlled Component)
- 表单的值完全由 React state 控制;
- 每次输入都会触发
onChange更新 state,渲染时把 state 作为 value; - 适合需要实时校验/联动的复杂表单。
非受控组件(Uncontrolled Component)
- 通过
defaultValue/ref直接操作 DOM 获取值; - React 不实时追踪值变化;
- 适合简单场景或第三方库集成。
- 通过
一面中常见问题是“说一下受控组件和非受控组件的区别”。
可以顺带提一下:复杂业务表单一般会选受控或受控 + 表单库(如 Formik/React Hook Form),因为更易做校验和状态管理。
常见面试题与参考答案
题 1:React 组件之间有哪些常见的通信方式?
参考答案要点:
- 父子通信:通过 props 传数据、回调;
- 兄弟通信:状态提升到共同父组件,或者使用 Context/状态管理库;
- 远亲组件:使用 Context 提供/消费全局状态,避免层层 props 传递。
可以顺带提到:
“我会优先用状态提升解决问题,只有在 props drilling 明显影响可维护性时才引入 Context 或全局 store。”
题 2:为什么 React 渲染列表时要加 key?为什么不建议用索引作为 key?
参考答案要点:
- key 是 React 识别列表中元素身份的依据,用于决定哪些节点复用、哪些需要创建/删除;
- 使用稳定的业务 id 可以让 React 正确复用组件实例;
- 使用索引作为 key,在列表前面插入/删除元素时,会导致:
- 组件实例错误复用;
- 内部状态(如输入框内容)与数据错位。
可以加一句:
“索引 key 在不会插入/删除,只会 push/pop 的纯展示列表里相对安全,但我在业务代码里基本都会优先选稳定 id。”
题 3:受控组件和非受控组件有什么区别?各适用于什么场景?
参考答案要点:
- 受控组件:表单值由 React state 单一来源控制,
value和onChange绑定在一起;- 适合需要实时校验/联动/回显的复杂表单;
- 非受控组件:值主要存储在 DOM 中,通过
ref/defaultValue获取或设置;- 适合简单表单、性能敏感场景或与非 React 组件集成。
可以补一句实际经验:
“中大型业务表单我会基本用受控 + 表单库,小而独立的输入框偶尔会用非受控简化实现,但要注意不要混乱两种模式。”