'ValueChanging' does not exist on type 'Readonly<{}>'

17,270

The most likely cause for this is that you don't specify the type for your component's state in its class definition, so it defaults to {}. You can fix it by declaring interfaces for the types of props and state and providing these as type arguments to React.Component:

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

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

You could provide the types directly between the < and >, but using interfaces usually leads to more readable code and promotes reuse. To prevent confusion with components it's also a good idea to use lower-case identifiers for the properties on props and state.

Share:
17,270

Related videos on Youtube

Ian Klinck
Author by

Ian Klinck

Updated on October 27, 2022

Comments

  • Ian Klinck
    Ian Klinck over 1 year

    I'm trying to implement a handler, in React, for a survey implemented in SurveyJS. This is for multiple-choice questions that may have answers like "None of the Above" or "Prefer Not To Answer". If one of those answers is selected, all other answers should be blanked out, and if a different answer is selected, these checkboxes should be cleared out. I'm doing fine with either one of these individually, but having problems with a question where both options are present, specifically when switching back & forth between the two special options.

    What I think is happening is that when one answer triggers the handler, and unchecks the other checkbox, it triggers the handler again. My solution is to set a state that indicates when the handler is in the middle of this process, and not do it again at that time.

    I got a JS solution for this here: https://github.com/surveyjs/editor/issues/125 - and below is my attempt to convert it to React. (Just the relevant parts of the code included.)

    However, on compile, it gives the following error:

    ERROR in [at-loader] ./src/components/Survey/SurveyContainer.tsx:55:19 TS2339: Property 'ValueChanging' does not exist on type 'Readonly<{}>'.

    I can't find anything about this specific error. Other references to the state (i.e. where I'm setting it) are working. Why can't I read it?

    Thanks!

    Component:

    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>
        );
      }
    }

    Container:

    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);
  • Ian Klinck
    Ian Klinck over 6 years
    Got past the compile issues, thanks! Still some logic debugging to do, but I can take it from there. :)