如何在React Native项目代码库中的哪个位置呈现组件?

时间:2018-07-17 20:49:42

标签: javascript reactjs react-native redux

我正在模拟器中运行一个应用,并尝试调试某些无法正确呈现的组件Questionnaire。该组件的render()方法包含以下内容:

  render() {
    if (this.props.questionCount === 0) {
      return (
        <View style={styles.loading}>
          <ActivityIndicator size="large" />
        </View>
      );
    }

因此,如果questionCount道具为零,它将仅“挂”在活动指示器上。这确实是我在模拟器中看到的。

要找出原因,我想找出Questionnaire的渲染位置,以便可以看到道具如何传递给它。但是,我在代码范围内搜索了<Questionnaire>,但是没有得到任何结果。

我知道该应用程序使用Redux,并且在React Native Debugger窗口中,好像正在调用Connect(Questionnaire)(见下文)。

找到呈现Questionnaire的代码部分以及传递给它的道具的正确方法是什么?

enter image description here

为完整起见,以下是Questionnaire组件的代码:

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
  ActivityIndicator,
  Dimensions,
  Platform,
  ScrollView,
  StatusBar,
  TouchableOpacity,
  View,
} from 'react-native';
import Modal from 'react-native-modal';
import { Actions } from 'react-native-router-flux';
import AndroidKeyboardAdjust from 'react-native-android-keyboard-adjust';

import { LOADING_SESSION_SMATTER, ADDRESS_FORM } from 'app/src/scenes';
import { loggingOut } from 'app/src/containers/login/login.actions';
import Text from 'app/src/components/text';
import { resetScene } from 'app/src/lib/utils';
import QuestionnaireHeader from './components/questionnaireHeader';
import QuestionWrapper from './questions/questionWrapper.ui';
import styles from './questionnaire.styles';


export default class Questionnaire extends Component {
  static propTypes = {
    fetchProfile: PropTypes.func.isRequired,
    index: PropTypes.number.isRequired, // 0-based question index
    isLastQuestion: PropTypes.bool.isRequired,
    nextScene: PropTypes.string.isRequired,
    questionCount: PropTypes.number.isRequired,
    questions: PropTypes.arrayOf(QuestionWrapper.propTypes.question).isRequired,
    saveAnswers: PropTypes.func.isRequired,
    setCompleted: PropTypes.func.isRequired,
    setIndex: PropTypes.func.isRequired,
    updateAnswer: PropTypes.func.isRequired,
    userId: PropTypes.number.isRequired,
    answers: PropTypes.shape({}),
    currentCategoryTitle: PropTypes.string,
    isDemo: PropTypes.bool,
    prompt: PropTypes.string,
  };

  static defaultProps = {
    answers: {},
    currentCategoryTitle: '', // may be blank if user logs out here
    isDemo: false,
    prompt: '', // may be blank if user logs out here
  };

  constructor(props) {
    super(props);

    this.state = {
      currentOffset: 0,
      currentIndex: props.index, // 0-based question index
      isModalVisible: false,
    };
  }

  componentDidMount() {
    this.scrollToCurrentQuestion(); // ios only. for android it must be called after ScrollView render animation.
    this.previousStatusBarStyle = StatusBar._defaultProps.barStyle.value;
    StatusBar.setBarStyle('light-content');
    if (Platform.OS !== 'ios') {
      AndroidKeyboardAdjust.setAdjustPan();
    }
    if (this.props.nextScene === ADDRESS_FORM) {
      this.props.fetchProfile();
    }
  }

  componentWillUnmount() {
    if (!loggingOut) {
      StatusBar.setBarStyle(this.previousStatusBarStyle);
    }
    if (Platform.OS !== 'ios') {
      AndroidKeyboardAdjust.setAdjustResize();
    }
  }

  completeQuestionnaire() {
    this.props.setCompleted(this.props.userId);
    resetScene(this.props.nextScene);
  }

  hideModal = () => {
    this.setState({ isModalVisible: false });
  };

  showModal = () => {
    this.setState({ isModalVisible: true });
  };

  scrollForward = () => {
    this.scrollView.scrollTo({ x: this.state.currentOffset + Dimensions.get('window').width });
    if (this.state.currentIndex < this.props.questions.length - 1) {
      this.updateIndex(this.state.currentIndex + 1);
    }
  };

  scrollBackward = () => {
    this.scrollView.scrollTo({ x: this.state.currentOffset - Dimensions.get('window').width });
    if (this.state.currentIndex > 0) {
      this.updateIndex(this.state.currentIndex - 1);
    }
  };

  scrollToCurrentQuestion = () => {
    if (!this.scrollView) return;
    this.scrollView.scrollTo({ x: this.state.currentIndex * Dimensions.get('window').width });
  };

  updateIndex(index) {
    this.setState({ currentIndex: index });
    this.props.setIndex(index);
  }

  updateAnswer = (question, answer, message) => {
    const isFinalSubmit = this.props.isLastQuestion || answer === 'Our surrogate';
    if (isFinalSubmit) {
      this.completeQuestionnaire();
    } else {
      this.scrollForward();
    }

    let formattedAnswer = answer;
    if (question.type === 'DATE') {
      formattedAnswer = new Date(answer).toISOString().substring(0, 10);
    } else if (question.type === 'SELECT_MULTIPLE') {
      formattedAnswer = answer.join('\t');
    }

    this.props.saveAnswers([{
      questionId: question.id,
      value: formattedAnswer,
      optionalText: message,
    }], isFinalSubmit);

    this.props.updateAnswer(question.id, answer);
  };

  skipQuestionnaire = () => {
    if (this.props.isDemo) {
      Actions.reset(LOADING_SESSION_SMATTER);
    } else {
      this.completeQuestionnaire();
      // not ideal but have to pass an array of answers to mark questionnaire completed
      this.props.saveAnswers([], true);
    }
  };

  handleScroll = (event) => {
    this.setState({ currentOffset: event.nativeEvent.contentOffset.x });
  };

  // not used by any questions (?)
  TooltipModal = () => {
    const currentQuestion = this.props.questions[this.state.currentIndex];

    return (
      <Modal
        isVisible={this.state.isModalVisible}
        backdropOpacity={0.4}
        onBackdropPress={this.hideModal}
        onBackButtonPress={this.hideModal}
      >
        <View style={styles.modal}>
          <View style={[styles.body, { width: 280 }]} >
            <View style={{ margin: 24 }}>
              <Text style={styles.question}>{currentQuestion.info ? currentQuestion.info.header : 'None'}</Text>
              <Text style={styles.modalText}>{currentQuestion.info ? currentQuestion.info.body : 'None'}</Text>
            </View>
            <View style={styles.divider} />
            <View style={styles.modalFooter}>
              <TouchableOpacity style={styles.nextButton} onPress={this.hideModal}>
                <Text style={styles.modalDone}>GOT IT</Text>
              </TouchableOpacity>
            </View>
          </View>
        </View>
      </Modal>
    );
  };

  render() {
    if (this.props.questionCount === 0) {
      return (
        <View style={styles.loading}>
          <ActivityIndicator size="large" />
        </View>
      );
    }

    return (
      <View style={styles.container}>
        <QuestionnaireHeader
          categoryTitle={this.props.currentCategoryTitle}
          onPressSkip={this.skipQuestionnaire}
        />

        <ScrollView
          horizontal
          pagingEnabled
          ref={(c) => { this.scrollView = c; }}
          showsHorizontalScrollIndicator={false}
          onContentSizeChange={this.scrollToCurrentQuestion}
          scrollEnabled={false}
          onScroll={this.handleScroll}
        >
          {this.props.questions.map((question, i) => (
            <QuestionWrapper
              key={question.id}
              questionNumber={i + 1}
              prompt={this.props.prompt}
              question={question}
              scrollBackward={this.scrollBackward}
              showModal={this.showModal}
              updateAnswer={this.updateAnswer}
              answers={this.props.answers}
              isLastQuestion={this.props.isLastQuestion}
              totalQuestions={this.props.questionCount}
            />

          ))}
        </ScrollView>

        <this.TooltipModal />
      </View>
    );
  }
}

1 个答案:

答案 0 :(得分:0)

根据Tholle的建议,我查看了Questionnaire的调查表的导入位置,该调查表位于questionnaire.container.js中,内容为:

import { connect } from 'react-redux';

import { trackEvent } from 'app/src/lib/analytics';
import { ADDRESS_FORM, NOTIFICATION_PERMISSIONS } from 'app/src/scenes';
import { fetchProfile } from 'app/src/containers/profile/profile.actions';
import { logOut } from 'app/src/containers/login/login.actions';
import { saveAnswers, setCompleted, setIndex, updateAnswer } from './questionnaire.actions';
import { getQuestionSet, getCurrentQuestion, getCurrentQuestionPrompt } from './questionnaire.selectors';
import Questionnaire from './questionnaire.ui';

const mapStateToProps = (state) => {
  const { user } = state;
  const { questionnaire } = user.onboarding;
  const { categories } = questionnaire;
  const questions = getQuestionSet(state);
  const currentIndex = questionnaire.index;
  const question = getCurrentQuestion(state);
  const currentCategory = question && categories.find(category => category.id === question.categoryId);
  const isLastQuestion = currentIndex === questions.length - 1;

  return {
    questions,
    question,
    categories,
    currentIndex,
    isLastQuestion,
    answers: questionnaire.answers,
    index: questionnaire.index, // 0-based index
    nextScene: questionnaire.hasShortEnrollment ? ADDRESS_FORM : NOTIFICATION_PERMISSIONS,
    isDemo: user.login.isDemo,
    prompt: getCurrentQuestionPrompt(state),
    answer: question && questionnaire.answers[question.id],
    questionCount: questions.length,
    currentCategoryTitle: currentCategory && currentCategory.title,
    loading: questionnaire.loading,
    userId: user.login.user && user.login.user.id,
  };
};

const mapDispatchToProps = dispatch => ({
  saveAnswers: (answers, isFinalSubmit) => {
    dispatch(saveAnswers(answers, isFinalSubmit));
  },
  setCompleted: (userId) => {
    trackEvent(userId, 'Completed Questionnaire');
    dispatch(setCompleted());
  },
  setIndex: (index) => {
    dispatch(setIndex(index));
  },
  updateAnswer: (questionId, answer) => {
    dispatch(updateAnswer(questionId, answer));
  },
  logOut: () => {
    dispatch(logOut());
  },
  fetchProfile: () => {
    dispatch(fetchProfile());
  },
});

export default connect(
  mapStateToProps,
  mapDispatchToProps,
)(Questionnaire);

实际上,Questionnaire也可以通过将其传递到connect()来呈现,不仅是将其括在<Questionnaire>之类的三角括号中。