Как загрузить дочерний компонент с помощью реквизита при первом рендере с помощью React Hooks - PullRequest
2 голосов
/ 19 сентября 2019

В моем проекте React Native я использую React Hooks в функциональном компоненте, чтобы установить состояние моего хранилища Redux.Затем значение из хранилища, которое я пытаюсь передать в дочерний компонент с помощью prop с именем timestamp

Проблема заключается в том, что дочерний компонент использует компонент DateTimePicker (из *).1006 *) и когда он изначально рендерит, реквизит 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;

Ответы [ 2 ]

2 голосов
/ 19 сентября 2019

В этом случае у вас есть два решения, каждое из которых будет зависеть от того, что лучше для вас или вашего проекта.

Первое решение устанавливает 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, возвращать нулевой компонент, в то время как дата равна нулю, или возвращать компонент, представляющий как 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?Используя этот хук, вы сообщаете React, что ваш компонент должен что-то делать после рендеринга.React запомнит переданную вами функцию (мы будем называть ее «эффектом») и вызовет ее позже после выполнения обновлений DOM.В этом случае мы устанавливаем заголовок документа, но мы также можем выполнять выборку данных или вызывать какой-либо другой императивный API.

Ссылка: https://reactjs.org/docs/hooks-effect.html

0 голосов
/ 19 сентября 2019

Вы можете установить начальное состояние для редуктора по умолчанию, начальное состояние может выглядеть следующим образом

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;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...