React Native:React Hooks首次渲染加载带Props子组件的方案
我完全懂你碰到的这个坑——Redux状态的初始化是在父组件的useEffect里触发的,而useEffect要等组件首次渲染完成后才会执行,所以第一次渲染时report.params.start_date还没被赋值,传给子组件的timestamp自然就是undefined,子组件直接去访问它的value和labels属性肯定会报错。
下面给你几个实用的解决方案,你可以根据项目情况选最合适的:
方案1:在Redux Store初始化时设置默认值
这是最彻底的解决办法,直接在reducer里给report.params设置初始值,这样组件首次渲染时就能拿到有效数据,不用等useEffect的dispatch:
// 示例reducer代码 import moment from 'moment'; const initialState = { report: { params: { start_date: moment().subtract(30, "days"), end_date: moment() } } }; const reportReducer = (state = initialState, action) => { // 你的reducer逻辑 switch(action.type) { case 'SET_REPORT_PARAMS': return { ...state, report: { ...state.report, params: action.payload } }; default: return state; } };
这样一来,父组件首次渲染时useSelector就能拿到初始化好的start_date,传给子组件的timestamp就不会是undefined了。
方案2:父组件给timestamp传递兜底默认值
如果不想修改reducer,也可以在传递props时给timestamp加个兜底值,确保子组件总能拿到有效的对象:
// 修改父组件的DateInput调用部分 <DateInput mode="date" timestamp={report.params?.start_date || { value: { date: moment().subtract(30, "days").toDate() }, labels: { date: moment().subtract(30, "days").format("DD.MM.YYYY") } }} />
这里用了可选链?.避免report.params不存在时的报错,再用||提供默认值,保证子组件拿到的timestamp始终是有结构的对象。
方案3:子组件添加安全判断与条件渲染
既然这个子组件要复用,那让它自己处理props.timestamp可能为undefined的情况会更健壮,比如用可选链操作符,或者加个加载状态:
const DateInput = props => { const [isPickerVisible, setIsPickerVisible] = useState(false); const showDateTimePicker = () => setIsPickerVisible(true); const hideDateTimePicker = () => setIsPickerVisible(false); const handleDateTimePicked = dateTime => { console.log(dateTime) hideDateTimePicker(); }; // 安全访问属性,避免undefined报错 const targetLabel = props.timestamp?.labels?.[props.mode]; const targetDate = props.timestamp?.value?.date; // 如果还没有有效数据,显示默认提示 if (!targetLabel || !targetDate) { return ( <Body> <Button transparent onPress={showDateTimePicker}> <Icon type="MaterialIcons" name={props.mode === 'time' ? 'access-time' : 'date-range' } /> <Text>选择{props.mode === 'time' ? '时间' : '日期'}</Text> </Button> </Body> ) } return ( <Body> <Button transparent onPress={showDateTimePicker}> <Icon type="MaterialIcons" name={props.mode === 'time' ? 'access-time' : 'date-range' } /> <Text>{targetLabel}</Text> </Button> <DateTimePicker date={targetDate} mode={props.mode} isVisible={isPickerVisible} onConfirm={handleDateTimePicked} onCancel={hideDateTimePicker} /> </Body> ); }
这样就算props.timestamp还没加载好,子组件也不会报错,而是显示友好的默认状态,等数据到位后自动更新。
小提醒
你父组件里的两个DateInput都传了start_date,是不是其中一个应该传end_date?别不小心写错了哦~
内容的提问来源于stack exchange,提问作者Sheixt

