在我的React Native项目中,我在功能组件中使用React Hooks来设置Redux存储上的状态。然后,我试图通过名为prop
timestamp
传递给子组件的存储中的值
问题是,子组件利用DateTimePicker
组件(来自react-native-modal-datetime-picker
)的优势,并且在最初呈现timestamp
道具时未定义,这会导致其出错。结果可以通过以下控制台日志查看:
undefined
undefined
Object {
"date": "2019-08-20",
"full": 2019-08-20T12:21:52.874Z,
"time": "01:21:52",
}
Object {
"date": "20.08.2019",
"time": "01:21 pm",
}
该组件被渲染两次。首先,道具没有定义,然后填充。
如何强制在第一个渲染之前加载props
?
我不想直接在子组件中调用状态,因为它将在各种用例中重复使用。
这是父组件:
import React, { useEffect } from 'react';
import { View } from 'react-native';
import { useSelector, useDispatch } from 'react-redux';
import DateInput from '../../../components/DateTimeInput';
import * as actions from '../../../redux/actions';
import moment from 'moment';
const ReportParamsScreen = props => {
const report = useSelector(state => state.report);
const dispatch = useDispatch();
useEffect(() => {
dispatch(actions.setReportParams({
start_date : moment().subtract(30, "days"),
end_date : moment()
}))
}, [dispatch]);
return (
<View style={styles.row}>
<View style={styles.column}>
<DateInput
mode="date"
timestamp={report.params.start_date}
/>
</View>
<View style={styles.column}>
<DateInput
mode="date"
timestamp={report.params.start_date}
/>
</View>
</View>
)
};
export default ReportParamsScreen;
这是子组件:
import React, { useEffect, useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { Body, Button, Text, Icon } from "native-base";
import DateTimePicker from "react-native-modal-datetime-picker";
const DateInput = props => {
const [isPickerVisible, setIsPickerVisible] = useState(false);
const showDateTimePicker = () => {
setIsPickerVisible(true);
};
const hideDateTimePicker = () => {
setIsPickerVisible(false);
};
const handleDateTimePicked = dateTime => {
console.log(dateTime)
hideDateTimePicker();
};
console.log(props.timestamp.value);
console.log(props.timestamp.labels); // These are the logs referened above.
return (
<Body>
<Button transparent onPress={showDateTimePicker}>
<Icon
type="MaterialIcons"
name={props.mode == 'time' ? 'access-time' : 'date-range' }
/>
<Text>{props.timestamp.labels[props.mode]}</Text>
</Button>
<DateTimePicker
date={props.timestamp.value.date}
mode={props.mode}
isVisible={isPickerVisible}
onConfirm={handleDateTimePicked}
onCancel={hideDateTimePicker}
/>
</Body>
);
}
export default DateInput;
答案 0 :(得分:2)
在这种情况下,您有两种解决方案,两者都将取决于对您或您的项目更有利的事物。
第一个解决方案是将start_date
和end_date
设置为关于Report的化简器的初始状态,但是我不知道这对您的业务/规则而言不是最佳解决方案,但是解决方案“更轻松”。
import React, { useEffect } from 'react';
import { View } from 'react-native';
import { useSelector, useDispatch } from 'react-redux';
import DateInput from '../../../components/DateTimeInput';
import * as actions from '../../../redux/actions';
import moment from 'moment';
const ReportParamsScreen = props => {
const report = useSelector(state => state.report);
const dispatch = useDispatch();
useEffect(() => {
dispatch(actions.setReportParams({
start_date : moment().subtract(30, "days"),
end_date : moment()
}))
}, [dispatch]);
if (!report.params.start_date) {
return null
}
return (
<View style={styles.row}>
<View style={styles.column}>
<DateInput
mode="date"
timestamp={report.params.start_date}
/>
</View>
<View style={styles.column}>
<DateInput
mode="date"
timestamp={report.params.start_date}
/>
</View>
</View>
)
};
export default ReportParamsScreen;
第二个解决方案是验证report.params.start_date
上的ReportParamsScreen
是否未定义,如果为true,则在日期为null时返回组件的null或返回表示为Loading
的组件,是个好习惯。
示例:
import React, { useEffect } from 'react';
import { View } from 'react-native';
import { useSelector, useDispatch } from 'react-redux';
import DateInput from '../../../components/DateTimeInput';
import * as actions from '../../../redux/actions';
import moment from 'moment';
const ReportParamsScreen = props => {
const report = useSelector(state => state.report);
const dispatch = useDispatch();
useEffect(() => {
dispatch(actions.setReportParams({
start_date : moment().subtract(30, "days"),
end_date : moment()
}))
}, [dispatch]);
if (!report.params.start_date) {
return <Loading />
}
return (
<View style={styles.row}>
<View style={styles.column}>
<DateInput
mode="date"
timestamp={report.params.start_date}
/>
</View>
<View style={styles.column}>
<DateInput
mode="date"
timestamp={report.params.start_date}
/>
</View>
</View>
)
};
export default ReportParamsScreen;
发生这种情况是因为useEffect
仅在渲染之后执行。
参考:
useEffect有什么作用?通过使用这个Hook,您可以告诉React您的组件在渲染后需要做一些事情。 React将记住您传递的函数(我们将其称为“效果”),并在执行DOM更新后稍后调用它。为此,我们可以设置文档标题,但也可以执行数据获取或调用其他命令性API。
答案 1 :(得分:0)
您可以为默认的reducer设置初始状态,初始状态可能如下所示
const initialState = {
report: {
params: {
start_date: moment().subtract(30, "days"),
end_date: moment()
}
}
}
function rootReducer(state = initialState, action){
//...
}
在父组件中,report
为undefined
是因为没有初始状态,该状态仅在第一次渲染后才设置,因为useEffect
仅在渲染后被调用
或者您可以为DateInput
提供默认值。
在父组件中
const ReportParamsScreen = props => {
const report = useSelector(state => state.report);
const dispatch = useDispatch();
useEffect(() => {
dispatch(actions.setReportParams({
start_date : moment().subtract(30, "days"),
end_date : moment()
}))
}, [dispatch]);
const getStartDate = () => {
if(report && report.params && report.params.start_date) {
return report.params.start_date
}
return moment()
}
return (
<View style={styles.row}>
<View style={styles.column}>
<DateInput
mode="date"
timestamp={getStartDate()}
/>
</View>
...
</View>
)
};
export default ReportParamsScreen;