React中如何抽离内部组件并跨函数传递useState状态变量
抽离嵌套子组件的实现方案
将定义在Chats内部的ChatLog抽离为独立组件的核心逻辑遵循React单向数据流规则:状态依然保留在父组件Chats中维护,只需要把ChatLog运行依赖的状态、方法通过props传入即可。
具体改造步骤
- 将
ChatLog的组件定义整体移动到Chats函数外部,作为独立的顶层函数组件 - 为
ChatLog声明props入参,接收所有组件运行需要的外部依赖:聊天记录数组、消息对齐判断方法、时间格式转换方法,后续按钮的更新、删除逻辑对应的处理函数也可以通过同样的方式传入 - 在父组件
Chats中渲染ChatLog时,将对应的依赖作为属性传递给子组件即可
改造后代码示例
// 抽离为独立组件的ChatLog,所有外部依赖通过props传入 const ChatLog = ({ msgs, checkSide, convertUnix }) => { return ( <div> {msgs.map((msgItem) => ( // 列表项必须加唯一key,这里用时间+用户名做临时唯一标识,有消息id优先用id <div className="chat-log" key={`${msgItem.time}-${msgItem.usr}`}> <p align={checkSide(msgItem.usr)}> <h2>{msgItem.msg}</h2> <h4>User: {msgItem.usr}</h4> <h4>Time: {convertUnix(msgItem.time)}</h4> <button>update</button> <button>delete</button> </p> </div> ))} </div> ) } function Chats() { const [msg, sendMsg] = useState(""); const [msgs1, sendMsgAll] = useState([]); useEffect(() => { onValue(ref(database), (snapshot) => { sendMsgAll([]); const data = snapshot.val(); if (data !== null) { // 原代码用map遍历但无返回值,换成forEach语义更准确 Object.values(data).forEach((msgItem) => { sendMsgAll((oldArray) => [...oldArray, msgItem]); }); } }); }, []) // 若checkSide、convertUnix是全局公共方法,直接在ChatLog文件顶部引入即可,无需通过props传递 // 若方法定义在Chats内部依赖当前组件作用域,则作为props传入 return ( <div className="ChatView"> <p> {/* 渲染时传入子组件需要的所有依赖 */} <ChatLog msgs={msgs1} checkSide={checkSide} convertUnix={convertUnix} /> </p> <p>{ChatInput()}</p> </div> ) };
注意事项
- 禁止在函数组件内部定义其他函数组件:父组件每次重渲染都会重新创建内部子组件的函数引用,React会判定这是全新的组件,触发子组件的全量卸载重挂载,不仅会造成性能浪费,还会丢失子组件内部的状态
- 列表渲染必须为每一项添加唯一的
key属性,帮助React在diff更新时准确匹配节点,避免出现内容错位、状态混乱的问题 - 如果后续子组件需要修改父组件维护的状态(比如点击删除按钮移除某条消息),只需要把父组件中定义的状态修改方法作为props传入子组件即可,不需要把状态转移到子组件内部维护
内容的提问来源于stack exchange,提问作者flying_kaktus
相关产品推荐
相关产品推荐

