web-dev-qa-db-fra.com

'ValueChanging' n'existe pas sur le type 'Readonly <{}>'

J'essaie d'implémenter un gestionnaire dans React pour une enquête implémentée dans SurveyJS. Ceci concerne les questions à choix multiples auxquelles peuvent être associées des réponses telles que "Aucune des réponses précédentes" ou "Préfère ne pas répondre". Si l'une de ces réponses est sélectionnée, toutes les autres réponses doivent être masquées, et si une réponse différente est sélectionnée, ces cases à cocher doivent être désélectionnées. Je me débrouille bien avec l'un ou l'autre de ces éléments individuellement, mais j'ai des problèmes avec une question où les deux options sont présentes, en particulier lors du basculement entre les deux options spéciales.

Je pense que ce qui se passe, c'est que lorsqu'une réponse déclenche le gestionnaire et désactive l'autre case à cocher, elle déclenche à nouveau le gestionnaire. Ma solution consiste à définir un état indiquant que le gestionnaire est au milieu de ce processus et à ne pas le répéter à ce moment-là.

J'ai une solution JS pour cela ici: https://github.com/surveyjs/editor/issues/125 - et voici ma tentative de la convertir en React. (Seules les parties pertinentes du code sont incluses.)

Cependant, lors de la compilation, cela donne l'erreur suivante:

ERREUR dans [at-loader] ./src/components/Survey/SurveyContainer.tsx:55:19 TS2339: La propriété 'ValueChanging' n'existe pas sur le type "En lecture seule <{}>".

Je ne trouve rien à propos de cette erreur spécifique. D'autres références à l'état (c'est-à-dire où je le mets) fonctionnent. Pourquoi je ne peux pas le lire?

Merci!

Composant:

import * as React from 'react';
import { Component } from 'react';
import { Survey, surveyStrings } from 'survey-react';
import 'whatwg-fetch';
import Marked from '../Marked';
import * as style from './style';

interface Props {
  surveyJson: object;
  complete: boolean;
  resultMarkdown: string;
  surveyTitle: string;
  sendSurveyAnswers: (answers: object[]) => void;
  noneOfTheAboveHandler: (survey: object, options: object) => void;
}

Survey.cssType = 'standard';
surveyStrings.progressText = '{0}/{1}';
surveyStrings.emptySurvey = '';

export default class SurveyComponent extends Component<Props, {}> {
  render() {
    const { surveyJson, sendSurveyAnswers, complete, surveyTitle, resultMarkdown, noneOfTheAboveHandler } = this.props;
    return (
      <style.Wrapper>
         { surveyJson && (!complete) &&
          <style.SurveyWrapper>
            <style.SurveyTitle>{ surveyTitle }</style.SurveyTitle>
            <style.Survey>
              <Survey
                onValueChanged={ noneOfTheAboveHandler }
                css={ style.surveyStyles }
                json={ surveyJson }
                onComplete={ sendSurveyAnswers }
              />
            </style.Survey>
          </style.SurveyWrapper>
         }
         { complete &&
         <style.Results>
           <Marked content={resultMarkdown} />
         </style.Results>
         }
      </style.Wrapper>
    );
  }
}

Récipient:

import * as React from 'react';
import { Component } from 'react';
import { connect } from 'react-redux';
import SurveyComponent from './SurveyComponent';

interface Props {
  id: string;
  surveyJson: object;
  complete: boolean;
  resultMarkdown: string;
  surveyTitle: string;
  getSurveyQuestions: (id: string) => void;
  sendSurveyAnswers: (answers: object[]) => void;
  noneOfTheAboveHandler: (survey: object, options: object) => void;
  clearSurvey: () => void;
}

class SurveyContainer extends Component<Props, {}> {
  constructor(props) {
    super(props);
    this.state = { ValueChanging: false };
  }

  componentDidMount() {
    this.noneOfTheAboveHandler = this.noneOfTheAboveHandler.bind(this);
    this.props.getSurveyQuestions(this.props.id);
  }

  specialValueSelected(options, specialValue) {
    const { question } = options;
    const prevValue = question.prevValue;
    const index = options.value.indexOf(specialValue);
    this.setState({ ValueChanging: true });
    //has special value selected
    if(index > -1) {
      //special value was selected before
      if(prevValue.indexOf(specialValue) > -1) {
        var value = question.value;
        value.splice(index, 1);
        question.value = value;
      } else {
        //special value select just now
        question.value = [specialValue];
      }
    }
    this.setState({ ValueChanging: false });
    return index > -1;
  }

  noneOfTheAboveHandler(survey, options) {
    const none = 'NA';
    const preferNotToAnswer = 'PN';
    const { question } = options;

    if(this.state.ValueChanging) {
      return;
    }

    if (!question || question.getType() !== 'checkbox') {
      return;
    }

    if (!question.prevValue || !options.value) {
      question.prevValue = options.value;
      return;
    }

    if (!this.specialValueSelected(options,none)) {
      this.specialValueSelected(options,preferNotToAnswer);
    }

    question.prevValue = options.value;
  }

  componentWillUnmount() {
    this.props.clearSurvey();
  }

  render() {
    return (
      <SurveyComponent
        noneOfTheAboveHandler={this.noneOfTheAboveHandler}
        {...this.props}
      />
    );
  }
}

const mapStateToProps = (state, ownProps) => ({
  surveyJson: state.survey.json,
  answers: state.survey.answers,
  resultMarkdown: state.survey.resultMarkdown,
  complete: state.survey.complete,
  surveyTitle: state.page && state.page.data ? state.page.data.title : ''
});

const mapDispatchToProps = dispatch => ({
  getSurveyQuestions: id => dispatch({ type: 'GET_SURVEY_QUESTIONS', id }),
  sendSurveyAnswers: answers => dispatch({ type: 'SEND_SURVEY_ANSWERS', answers: answers.data }),
  clearSurvey: () => dispatch({ type: 'CLEAR_SURVEY' })
});

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

12
Ian Klinck

La cause la plus probable est que vous ne spécifiez pas le type de l'état de votre composant dans sa définition de classe. Par conséquent, la valeur par défaut est {}. Vous pouvez résoudre ce problème en déclarant des interfaces pour les types d'accessoires et d'état et en les fournissant comme arguments de type à React.Component:

interface MyComponentProps { /* declare your component's props here */ }
interface MyComponentState { ValueChanging :  boolean }

class MyComponent extends React.Component<MyComponentProps, MyComponentState> {
  constructor(props) {
  ...

Vous pouvez fournir les types directement entre < et >, mais l'utilisation d'interfaces entraîne généralement un code plus lisible et favorise la réutilisation. Pour éviter toute confusion avec les composants, il est également judicieux d'utiliser des identificateurs minuscules pour les propriétés des accessoires et de l'état.

24
Oblosys

FYI: Nous avons déjà introduit cette fonctionnalité dans la bibliothèque SurveyJS.

Voici l'exemple de réaction avec les fonctionnalités "Tout sélectionner" et "Aucun des éléments précédents", prêtes à l'emploi, sans code personnalisé.

Je vous remercie!

0
Andrew Telnov