前言
- 我们在开发的时候,有时候会遇到,A页面跳转到B页面,B页面改一些数据(例如:收藏状态),回到A页面的时候不想刷新A页面,并看到最新的数据状态;
- 对于以上场景,有以下几种解决方案;
方法一:EventChannel(推荐)
PageA.jsx - 跳转页面
import React, { useState } from 'react'
import { View, Button, Text } from '@tarojs/components'
import Taro from '@tarojs/taro'
const PageA = () => {
const [receivedData, setReceivedData] = useState(null)
const goToPageB = () => {
Taro.navigateTo({
url: '/pages/pageB/index',
events: {
// 监听返回数据
// ⚠️ 这里监听的事件名 必须和 子页面绑定的事件名称相同
onReturnData: (data) => {
console.log('接收到返回数据:', data)
setReceivedData(data)
},
// 可以监听多个事件
onSelectItem: (item) => {
console.log('选中的项目:', item)
}
}
})
}
return (
<View className="page-a">
<Button onClick={goToPageB}>跳转到页面B</Button>
{receivedData && (
<View className="received-data">
<Text>接收到的数据:</Text>
<Text>{JSON.stringify(receivedData)}</Text>
</View>
)}
</View>
)
}
export default PageA
PageB.jsx - 返回页面
import React, { useState, useEffect } from 'react'
import { View, Button, Input } from '@tarojs/components'
import Taro from '@tarojs/taro'
const PageB = () => {
// 可以使用 useState 或 useRef 存储 EventChannel
const [eventChannel, setEventChannel] = useState(null)
const [inputValue, setInputValue] = useState('')
const [count, setCount] = useState(0)
useEffect(() => {
// 获取 EventChannel
const channel = Taro.getCurrentInstance().page?.getOpenerEventChannel?.()
if (channel) {
setEventChannel(channel)
}
}, [])
const handleReturn = () => {
if (eventChannel) {
// 发送数据给上个页面
eventChannel.emit('onReturnData', {
inputValue,
timestamp: Date.now(),
source: 'pageB'
})
}
// 返回上个页面
Taro.navigateBack()
}
const handleSelectItem = (item) => {
if (eventChannel) {
eventChannel.emit('onSelectItem', item)
}
}
// ---- 页面销毁传递参数 Start ----
// 若是使用小程序的导航栏的返回按钮,可以在页面销毁的时候,向父页面传递参数
// 需要注意的是,useUnload 的参数若是依赖于一些数据,需要使用 useCallback 对函数进行缓存
const handleBack = useCallback(() => {
if (eventChannel) {
eventChannel?.emit('onReturnPageA', { name: 'PageA', count })
console.log('数据发送成功')
}
}, [eventChannel, count])
useUnload(handleBack)
// ---- 页面销毁传递参数 End ----
return (
<View className="page-b">
<Input
value={inputValue}
onInput={(e) => setInputValue(e.detail.value)}
placeholder="输入要传递的数据"
/>
<Button onClick={handleReturn}>返回并传递数据</Button>
<Button onClick={() => handleSelectItem({ id: 1, name: '选项1' })}>
选择选项1
</Button>
<Button onClick={() => setCount(v => v++)}>
改变count
</Button>
<Button onClick={() => handleSelectItem({ id: 2, name: '选项2' })}>
选择选项2
</Button>
</View>
)
}
export default PageB
方法二:使用 Zustand 状态管理
store/index.js
import { create } from 'zustand'
const useAppStore = create((set, get) => ({
// 页面返回数据
pageReturnData: null,
// 设置返回数据
setPageReturnData: (data) => set({ pageReturnData: data }),
// 清除返回数据
clearPageReturnData: () => set({ pageReturnData: null }),
// 获取并清除返回数据
getAndClearReturnData: () => {
const data = get().pageReturnData
set({ pageReturnData: null })
return data
}
}))
export default useAppStore
PageA.jsx - 使用状态管理
import React, { useEffect } from 'react'
import { View, Button, Text } from '@tarojs/components'
import Taro, { useDidShow } from '@tarojs/taro'
import useAppStore from '../store'
const PageA = () => {
const { pageReturnData, clearPageReturnData } = useAppStore()
// 页面显示时检查返回数据
useDidShow(() => {
if (pageReturnData) {
console.log('接收到返回数据:', pageReturnData)
// 处理数据后清除
handleReturnData(pageReturnData)
clearPageReturnData()
}
})
const handleReturnData = (data) => {
// 处理返回的数据
Taro.showToast({
title: `接收到: ${data.message}`,
icon: 'success'
})
}
const goToPageB = () => {
Taro.navigateTo({
url: '/pages/pageB/index'
})
}
return (
<View className="page-a">
<Button onClick={goToPageB}>跳转到页面B</Button>
</View>
)
}
export default PageA
PageB.jsx - 设置状态并返回
import React, { useState } from 'react'
import { View, Button, Input } from '@tarojs/components'
import Taro from '@tarojs/taro'
import useAppStore from '../store'
const PageB = () => {
const [message, setMessage] = useState('')
const setPageReturnData = useAppStore(state => state.setPageReturnData)
const handleReturn = () => {
// 设置要传递的数据
setPageReturnData({
message,
timestamp: Date.now(),
type: 'user_input'
})
// 返回上个页面
Taro.navigateBack()
}
return (
<View className="page-b">
<Input
value={message}
onInput={(e) => setMessage(e.detail.value)}
placeholder="输入消息"
/>
<Button onClick={handleReturn}>返回并传递消息</Button>
</View>
)
}
export default PageB
方法三:自定义 Hook 封装
hooks/usePageReturn.js
import { useState, useEffect } from 'react'
import Taro, { useDidShow } from '@tarojs/taro'
// 全局存储返回数据
let globalReturnData = new Map()
export const usePageReturn = (pageKey) => {
const [returnData, setReturnData] = useState(null)
useDidShow(() => {
const data = globalReturnData.get(pageKey)
if (data) {
setReturnData(data)
globalReturnData.delete(pageKey)
}
})
const setReturnDataForPage = (targetPageKey, data) => {
globalReturnData.set(targetPageKey, data)
}
const clearReturnData = () => {
setReturnData(null)
}
return {
returnData,
setReturnDataForPage,
clearReturnData
}
}
// 导航并设置返回监听
export const navigateToWithReturn = (url, pageKey, onReturn) => {
return Taro.navigateTo({
url,
events: {
returnData: (data) => {
if (onReturn) {
onReturn(data)
}
}
}
})
}
使用自定义 Hook
// PageA.jsx
import React from 'react'
import { View, Button } from '@tarojs/components'
import Taro from '@tarojs/taro'
import { usePageReturn } from '../hooks/usePageReturn'
const PageA = () => {
const { returnData, clearReturnData } = usePageReturn('pageA')
useEffect(() => {
if (returnData) {
console.log('接收到返回数据:', returnData)
// 处理数据
clearReturnData()
}
}, [returnData])
const goToPageB = () => {
Taro.navigateTo({
url: '/pages/pageB/index?fromPage=pageA'
})
}
return (
<View>
<Button onClick={goToPageB}>跳转到页面B</Button>
</View>
)
}
export default PageA
最佳实践建议
-
简单场景:使用 EventChannel(方法一)
-
复杂应用:使用状态管理(方法二)
-
多页面复用:封装自定义 Hook(方法三)
-
数据量大:避免使用 URL 参数,选择状态管理
-
临时数据:使用 EventChannel,自动清理
-
持久数据:结合本地存储使用
注意事项
- EventChannel 只在
navigateTo 时可用,redirectTo 不支持
- 状态管理要注意及时清理数据,避免内存泄漏
- 复杂对象传递时注意序列化问题
- 考虑页面栈的层级关系,避免数据传递错乱