反应-在未安装的组件上设置状态

时间:2018-11-16 22:23:32

标签: javascript reactjs

我尝试了很多方法使其有效,但没有成功。可能我不了解React的想法,或者我错过了一些东西。我的孩子通过回调尝试修改全局/父状态,然后得到了众所周知的错误。

  

index.js:1452警告:无法在已卸载的组件上调用setState(或forceUpdate)。这是空操作,但它表明应用程序中发生内存泄漏。要修复此问题,请取消componentWillUnmount方法中的所有订阅和异步任务。

我尝试使用_isMounted变量,因此在执行setState之前:

if (this.isMounted()) { 
  this.setState({...});
}

不幸的是,它不起作用。

问题是从子级调用功能 this.handleChangeAlgorithms 时。

我的代码

class ConfigurationForm extends Component {
  constructor(props) {
    super(props);
    this.state = {
      choosenAlgorithm: null,
      htmlType: null,
      customHtml: null,
      isOpenModal: false,
      generatedHTMlConfig: {
        headers: 1,
        paragraphs: 1,
        buttons: 1,
        links: 1,
        inputs: 1,
        images: 1,
      },
    };
  }

  handleChangeAlgorithms = event => {
    let algorithmName = event.target.value;
    let newChoosenAlgorithm = this.props.algorithms.filter(
      e => e.value === algorithmName,
    )[0];
    // this.setState({choosenAlgorithm: newChoosenAlgorithm});
    this.props.callbackConfigurationForm(algorithmName);
  };

  handleChangeHtmlType(event) {
    let newHtmlType = event.target.value;
    console.log(newHtmlType);
    if (newHtmlType === "default") {
      this.setState({ isOpenModal: true });
    } else {
      this.setState({ htmlType: newHtmlType });
    }
  }

  handleUserFile(event) {
    let file = event.target.files[0];
    this.setState({ customHtml: file });
    this.props.callbackConfigurationFormPreview(file);
  }

  handleSubmit(event) {
    event.preventDefault();
    let htmlContent = null;
    let htmltypeKey = Object.keys(HtmlType).find(
      key => HtmlType[key] === HtmlType.default,
    );
    console.log(htmltypeKey);
    // if (this.state.htmlType === 'default'){
    // htmlContent = this.loadTemplate();
    htmlContent = generateHTML(this.state.generatedHTMlConfig);
    console.log(htmlContent);
    // } else {
    //     htmlContent=this.state.customHtml;
    // }
    let config = {
      algorithm: this.state.choosenAlgorithm,
      html: htmlContent,
    };
    console.log(config);
    this.props.callbackConfigurationForm(config);
  }

  loadTemplate() {
    let loadedTemplate = null;
    let file = "html-templates/example.html";
    let request = new XMLHttpRequest();
    request.open("GET", file, false);
    request.send(null);
    if (request.status === 200) {
      loadedTemplate = request.responseText;
    }
    return loadedTemplate;
  }

  renderHtmlTypesList = () => {
    let list = [];
    for (const htmlKey of Object.keys(HtmlType)) {
      list.push(
        <option key={htmlKey} value={htmlKey}>
          {HtmlType[htmlKey]}
        </option>,
      );
    }
    return list;
  };

  generateHTMLFromPopup() {
    this.setState({ isOpenModal: false });
  }

  changeHeaders(event, type) {
    console.log(type);
    console.log(event.target.value);
    let prevConfig = { ...this.state.generatedHTMlConfig };
    switch (type) {
      case "Headers":
        prevConfig.headers = event.target.value;
        this.setState({ generatedHTMlConfig: prevConfig });
        break;
      case "Paragraphs":
        prevConfig.paragraphs = event.target.value;
        this.setState({ generatedHTMlConfig: prevConfig });
        break;
      case "Buttons":
        prevConfig.buttons = event.target.value;
        this.setState({ generatedHTMlConfig: prevConfig });
        break;
      case "Links":
        prevConfig.links = event.target.value;
        this.setState({ generatedHTMlConfig: prevConfig });
        break;
      case "Inputs":
        prevConfig.inputs = event.target.value;
        this.setState({ generatedHTMlConfig: prevConfig });
        break;
      case "Images":
        prevConfig.images = event.target.value;
        this.setState({ generatedHTMlConfig: prevConfig });
        break;
    }
  }

  render() {
    return (
      <Row>
        {/* todo 12.11.2018 extract to another component! */}
        <Modal
          open={this.state.isOpenModal}
          header="Generate Html"
          actions={
            <div>
              <Button
                modal="close"
                waves="light"
                className="red lighten-2"
              >
                Cancel
              </Button>
              <Button
                modal="close"
                waves="light"
                className="blue"
                onClick={this.generateHTMLFromPopup.bind(this)}
              >
                <Icon left>build</Icon>Generate
              </Button>
            </div>
          }
        >
          <p>Choose HTML elements for generated HTML.</p>
          <Input
            type="number"
            label="Headers"
            value={this.state.generatedHTMlConfig.headers}
            onChange={e => this.changeHeaders(e, "Headers")}
          />
          <Input
            type="number"
            label="Paragraphs"
            value={this.state.generatedHTMlConfig.paragraphs}
            onChange={e => this.changeHeaders(e, "Paragraphs")}
          />
          <Input
            type="number"
            label="Buttons"
            value={this.state.generatedHTMlConfig.buttons}
            onChange={e => this.changeHeaders(e, "Buttons")}
          />
          <Input
            type="number"
            label="Links"
            value={this.state.generatedHTMlConfig.links}
            onChange={e => this.changeHeaders(e, "Links")}
          />
          <Input
            type="number"
            label="Inputs"
            value={this.state.generatedHTMlConfig.inputs}
            onChange={e => this.changeHeaders(e, "Inputs")}
          />
          <Input
            type="number"
            label="Images"
            value={this.state.generatedHTMlConfig.images}
            onChange={e => this.changeHeaders(e, "Images")}
          />
        </Modal>
        <h2>Algorithm</h2>
        <Row>
          <Input
            s={12}
            type="select"
            label="Select algorithm"
            defaultValue=""
            onChange={this.handleChangeAlgorithms}
          >
            <option value="" disabled>
              Choose an algorithm
            </option>
            {this.props.algorithms.map(item => (
              <option key={item.value} value={item.value}>
                {item.name}
              </option>
            ))}
          </Input>
        </Row>
        {this.state.choosenAlgorithm ? (
          <Collapsible popout>
            <CollapsibleItem header="Details" icon="notes">
              <ol>
                {this.state.choosenAlgorithm.details.steps.map(
                  (step, index) => (
                    <li key={index}>{step}</li>
                  ),
                )}
              </ol>
            </CollapsibleItem>
          </Collapsible>
        ) : null}
        <h2>HTML to obfuscate</h2>
        <Row>
          <Input
            s={12}
            type="select"
            label="HTML type"
            defaultValue=""
            onChange={this.handleChangeHtmlType}
          >
            <option value="" disabled>
              Choose HTML
            </option>
            {this.renderHtmlTypesList()}
          </Input>
        </Row>
        {this.state.htmlType === "custom" ? (
          <Row>
            <Input
              type="file"
              label="File"
              onChange={this.handleUserFile}
            />
          </Row>
        ) : null}
        <div className="center-align">
          <Button type={"button"} onClick={this.handleSubmit}>
            Process<Icon left>autorenew</Icon>
          </Button>
        </div>
      </Row>
    );
  }
}

class App extends Component {
  _isMounted = false;

  constructor(props) {
    super(props);
    this.state = {
      isMounted: false,
      //export to enum
      algorithms: [
        {
          name: "Html to Javascript",
          value: "1",
          details: {
            steps: [
              "Split HTML file line by line.",
              "Replace white characters.",
              "Create function which add lines to document using document.write function.",
            ],
          },
        },
        {
          name: "Html to Unicode characters",
          value: "2",
          details: {
            steps: [
              "Create js function encoding characters to Unicode characters.",
              "Create decoding function.",
              "Add output from decoding function to HTML.",
            ],
          },
        },
        {
          name: "Html to escape characters",
          value: "3",
          details: {
            steps: [
              "Change endcoding using escape javascript function.",
              "Decode using unescape javascript function.",
              "Add element to HTML.",
            ],
          },
        },
        {
          name:
            "Using own encoding and decoding function. [NOT IMPLEMENTED YET]",
          value: "4",
          details: {
            steps: [
              "Encode HTML using own function.",
              "Save encoded content into js variable.",
              "Decode using own decoding function.",
              "Add element to HTML document.",
            ],
          },
        },
        {
          name: "Combine above methods [NOT IMPLEMENTED YET]",
          value: "5",
          details: {
            steps: ["To be done..."],
          },
        },
      ],
      previewHtml: null,
      obfuscationConfig: null,
      activeTab: 1,
    };
  }

  componentDidMount() {
    this._isMounted = true;
  }

  componentWillUnmount() {
    this._isMounted = false;
  }

  processDataFromConfigurationForm = config => {
    console.info(config);
    if (this._isMounted) {
      this.setState({
        test: Date.now(),
        // obfuscationConfig: config,
        // doObfuscation: true,
        // activeTab:3,
        // previewHtml: config.html
      });
    }
  };

  render() {
    return (
      <div>
        <header className="App">
          <h1>HTML obfuscator</h1>
        </header>
        <Tabs>
          <Tab
            title="Configuration"
            active={this.state.activeTab === 1}
          >
            <ConfigurationForm
              algorithms={this.state.algorithms}
              config={this.state.obfuscationConfig}
              callbackConfigurationForm={
                this.processDataFromConfigurationForm
              }
            />
          </Tab>
          <Tab
            title="HTML Preview"
            active={this.state.activeTab === 2}
            disabled={!this.state.previewHtml}
          >
            <HTMLPreview previewHtml={this.state.previewHtml} />
          </Tab>
          <Tab
            title="Result"
            active={this.state.activeTab === 3}
            disabled={!this.state.obfuscationConfig}
          >
            {this.state.obfuscationConfig ? (
              <ObfuscationOutput
                config={this.state.obfuscationConfig}
              />
            ) : null}
          </Tab>
        </Tabs>
      </div>
    );
  }
}

export default App;

The error

2 个答案:

答案 0 :(得分:0)

尝试向父类添加构造函数,实际上,您可以在第一次修改之前初始化组件状态:

class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            test1: Date.now()
        };
    }
    // here you can update the state as you want, for example
    foobar(event) {
        this.setState({test1: event.target.value });
    }
}

希望它可以帮助您...

答案 1 :(得分:0)

在生命周期方法componentDidMount()内部设置状态应该可以解决问题。

哦,我没有看到有人已经建议过,很高兴你能做到