【问题标题】:'ValueChanging' does not exist on type 'Readonly<{}>''Readonly<{}>' 类型上不存在'ValueChanging'
【发布时间】:2018-03-01 13:00:14
【问题描述】:

我正在尝试在 React 中实现一个处理程序,用于在 SurveyJS 中实现的调查。这是针对可能有“以上都不是”或“不想回答”等答案的多项选择题。如果选择了其中一个答案,则应取消所有其他答案,如果选择了不同的答案,则应清除这些复选框。我对其中任何一个都做得很好,但是在两个选项都存在的问题上遇到了问题,特别是在两个特殊选项之间来回切换时。

我认为正在发生的是,当一个答案触发处理程序并取消选中另一个复选框时,它会再次触发处理程序。我的解决方案是设置一个状态,指示处理程序何时处于此过程的中间,并且在那个时候不再这样做。

我在这里找到了一个 JS 解决方案:https://github.com/surveyjs/editor/issues/125 - 下面是我尝试将其转换为 React。 (仅包括代码的相关部分。)

但是,在编译时,它给出了以下错误:

[at-loader] 中的错误 ./src/components/Sur​​vey/SurveyContainer.tsx:55:19 TS2339:类型上不存在属性“ValueChanging” '只读'。

我找不到有关此特定错误的任何信息。对状态的其他引用(即我设置它的位置)正在工作。为什么我看不懂?

谢谢!

组件:

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

容器:

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

【问题讨论】:

  • 如何取消选中其他复选框?
  • 它不应该那么难和冗长。可能如果您可以发布您的组件,我们可以制定一个更小更简单的出路
  • 当我为一个问题设置 question.value 时,它​​变成了完整的答案列表。 (例如,如果我将其设置为“NA”,则仅选中“NA”复选框,而所有其他复选框均未选中。)我将发布完整的组件。

标签: reactjs typescript


【解决方案1】:

最可能的原因是您没有在其类定义中指定组件状态的类型,因此它默认为{}。你可以通过声明 props 和 state 类型的接口并将它们作为类型参数提供给 React.Component 来修复它:

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

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

您可以直接在&lt;&gt; 之间提供类型,但使用接口通常会导致代码更具可读性并促进重用。为了防止与组件混淆,对 props 和 state 的属性使用小写标识符也是一个好主意。

【讨论】:

  • 解决了编译问题,谢谢!仍然需要进行一些逻辑调试,但我可以从那里着手。 :)
【解决方案2】:

仅供参考:我们有时会将此功能引入到 SurveyJS 库中。

这里是the react example,具有“全选”和“以上均无”功能,开箱即用,无需自定义代码。

谢谢!

【讨论】:

    猜你喜欢
    • 2020-11-07
    • 2020-01-24
    • 2022-10-16
    • 1970-01-01
    • 2022-01-23
    • 2021-01-11
    • 1970-01-01
    • 2018-10-11
    • 2019-12-11
    相关资源
    最近更新 更多