根据另一个组件的状态动态创建一个新组件

时间:2020-08-07 21:43:47

标签: reactjs

第一个计时器在StackOverflow上!我正在尝试为我的一个投资组合项目模拟一个终端接口。我设想的终端方式是,每个终端盒都有一个带有几个键/值对的状态对象。理想情况下,当有人在接线盒输入表单中输入文本时,输入将被禁用,并且将基于状态已保存的userInput文本以动态响应在屏幕上呈现新的接线盒。我被困住的地方:

  1. 一旦userInput状态已更新,如何在屏幕上的前一个框下面显示一个新的终端框?
  2. 在渲染之前,如何将新渲染的接线盒的初始状态重新设置为默认状态,除了“输出”会重新设置为我设置的适当响应之外?
  3. 如何访问上一个接线盒中的状态,以便可以“读取”存储在其中的userInput,从而可以确定对该输入的适当响应?

我提供了以下每个组件的副本:

App.js

import React from "react";
import Terminal from "./components/Terminal";
import "./App.css";

class App extends React.Component {
  render() {
    return (
      <div>
        <Terminal />
      </div>
    );
  }
}

export default App;

Terminal.js

import React, { Component } from "react";
import Form from "./Form";
import Falcon from "./Falcon";
import Messages from "./Alerts/Messages";

class Terminal extends Component {
  state = {
    output: Messages.intro,
    userInput: "",
    isComplete: false,
    isDisabled: "",
  };

  markComplete = () => {
    this.setState({
      isComplete: true,
    });
  };

  onSubmit = (event, userInput) => {
    event.preventDefault();
    this.setState({
      userInput: userInput,
      isDisabled: "disabled",
    });
  };

  render() {
    return (
      <div>
        <Falcon
          output={this.state.output}
          markComplete={this.markComplete}
          isComplete={this.state.isComplete}
        />
        <p />
        <Form
          input={this.state.userInput}
          onSubmit={this.onSubmit}
          isComplete={this.state.isComplete}
          isDisabled={this.state.isDisabled}
        />
        <p />
      </div>
    );
  }
}

export default Terminal;

Falcon.js 注意 :您会看到下面有一个“ Typed”组件-这是Matt Boldt的一部分我正在用来模拟打字的Typed.js(其中react-typed是分支包)软件包。

import React, { Component } from 'react'
import Typed from 'react-typed'

class Falcon extends Component {
  state = {
    output: this.props.output,
  };

  render() {
    return (
      <div>
        <Typed
          strings={[this.state.output]}
          typeSpeed={40}
          onComplete={(self) => {
            self.cursor.remove();
            this.props.markComplete();
          }}
        />
      </div>
    );
  }
}

export default Falcon;

Form.js

import React from "react";

class Form extends React.Component {
  state = {
    input: this.props.input,
  };

  render() {
    return (
      <form
        style={{
          display: this.props.isComplete === false ? "none" : "",
        }}
        onSubmit={(event) => {
          this.props.onSubmit(event, this.state.input);
        }}
      >
        {"> "}
        <input
          ref={(input) => input && input.focus()}
          type="text"
          disabled={this.props.isDisabled}
          style={{
            border: "none",
            outline: "none",
            backgroundColor: "#FFF",
            color: "#000",
          }}
          value={this.state.input}
          onChange={(event) => this.setState({ input: event.target.value })}
        />
      </form>
    );
  }
}

export default Form;

非常感谢您提供的见解或指导!感谢您帮助这个“新手”!

1 个答案:

答案 0 :(得分:0)

欢迎使用StackOverflow!我做了一个codesandbox demo,做了一些更改。

在开发React应用程序时,将UI(DOM元素)建模为内部状态的函数是一个好习惯。您更新状态,UI会自动更改,它会反应进行更新。

也就是说,您可能希望考虑仅将表单用于终端底部的实际输入元素。 “过去缓冲区”只是一个数组,只能通过用户输入和程序输出来增加其内容。另一个好的做法(实际上是一条诫命!)是永远不要改变状态。因此,如果要更新阵列,则可以从头开始创建一个新阵列,如:

this.setState((state) => ({
  conversation: [
    ...state.conversation, // we spread the previous state into the new one
    { text: state.userInput, id: faker.random.uuid(), type: "input" } // the last element is appended
  ]
}));}

请注意setState(在类组件中)如何仅计划您所使用的字段的更新。随着应用程序的扩展,您可能需要限制此数组的长度。

终端组件可能像这样:

class Terminal extends Component {
  state = {
    output: "Messages.intro",
    userInput: "",
    isComplete: false,
    isDisabled: "",
    conversation: [] // couldn't think of a nice name :(
  };

  markComplete = () => {
    this.setState({
      isComplete: true
    });
  };

  onChange = (event) => {
    this.setState({ userInput: event.target.value });
  };

  onSubmit = (event) => {
    event.preventDefault();

    this.setState((state) => ({
      userInput: "",
      conversation: [
        ...state.conversation,
        { text: state.userInput, id: faker.random.uuid(), type: "input" }
      ]
    }));
  };

  render() {
    const { conversation, userInput, output, isComplete } = this.state;

    return (
      <div>
        <Falcon
          output={output}
          markComplete={this.markComplete}
          isComplete={isComplete}
        />

        <p />

        // This is not really a form. Should be changed to another readonly element
        {conversation.map((item, index) => (
          <Form
            key={item.id}
            input={item.text}
            onSubmit={this.onSubmit}
            isComplete={isComplete}
            isDisabled
          />
        ))}

        <p />

        <Form
          input={userInput}
          onSubmit={this.onSubmit}
          isComplete={isComplete}
          isDisabled={false}
          onChange={this.onChange}
        />
        <p />
      </div>
    );
  }
}
相关问题