无法在嵌套对象reactjs上设置状态

时间:2020-05-11 03:49:44

标签: reactjs input state onchange nested-object

我在从输入中更新嵌套对象的状态时遇到问题。

已更新更多代码。我正在尝试建立一个多步骤表单。第一页获取团队信息,第二页获取玩家信息。

父组件:

export class MainForm extends Component {
state = {
    step: 1,
    teamName: '',
    teamManagerName: '',
    teamManagerEmail: '',
    player: [{
        firstName: '',
        lastName: '',
        email: '',
        height: ''
    }]
}

// proceed to next step
nextStep = () => {
    const { step } = this.state;
    this.setState({
        step: step + 1
    })
}

// go to previous step
prevStep = () => {
    const { step } = this.state;
    this.setState({
        step: step - 1
    })
}

// handle fields change
handleChange = input => e => {
    this.setState({[input]: e.target.value});
}

render() {
    const { step } = this.state;
    const { teamName, teamManagerName, teamManagerEmail, player: { firstName, lastName, email, height}} = this.state;
    const values = {teamName, teamManagerName, teamManagerEmail, player: { firstName, lastName, email, height}};

    switch(step) {
        case 1:
            return (

                <FormTeamDetails
                    nextStep={this.nextStep}
                    handleChange={this.handleChange}
                    values={values}
                />
                )
        case 2:
            return (
               <FormPlayerDetails
                    nextStep={this.nextStep}
                    prevStep={this.prevStep}
                    handleChange={this.handleChange}
                    values={values}
               />
            )
        case 3:
            return (
                <Confirm
                    nextStep={this.nextStep}
                    prevStep={this.prevStep}
                    values={values}
           />
            )
        case 4:
            return (
                <h1>Scuccess!</h1>
            )
    }

}

}

下一个代码段是表单的第一页和子组件之一。 handleChange函数在这里成功完成其工作。

export class FormTeamDetails extends Component {
continue = e => {
    e.preventDefault();
    this.props.nextStep();
}

render() {
    const { values, handleChange } = this.props;
    console.log(this.props)
    return (
        <Container>
            <Form>
                <FormGroup>
                    <Label for="teamName">Team Name</Label>
                    <Input 
                        type="teamName" 
                        name="teamName" 
                        onChange={handleChange('teamName')} 
                        defaultValue={values.teamName} />
                    <Label for="teamManagerName">Team Manager Name</Label>
                    <Input 
                        type="teamManagerName" 
                        name="teamManagerName" 
                        onChange={handleChange('teamManagerName')} 
                        defaultValue={values.teamManagerName}
                        />
                    <Label for="teamManagerEmail">Team Manager Email</Label>
                    <Input 
                        type="teamManagerEmail" 
                        name="teamManagerEmail" 
                        onChange={handleChange('teamManagerEmail')} 
                        defaultValue={values.teamManagerEmail} />

                    <Button 
                        label="Next"
                        // primary="true"
                        onClick={this.continue}
                        style={{float: 'right'}}>Next</Button>
                </FormGroup>
            </Form>
        </Container>

    )
}

}

这是表单的第二页,也是我遇到的问题:

export class FormPlayerDetails extends Component {
continue = e => {
    e.preventDefault();
    this.props.nextStep();
}

back = e => {
    e.preventDefault();
    this.props.prevStep();
}

render() {
    const { values: { player }, handleChange, values } = this.props;

    return (
        <Container>
            <h3>Add players</h3>
            <Form>
            <Row form>
                <Col md={3}>
                <FormGroup>
                    <Input type="firstName" 
                    name="firstName" 
                    id="firstName" 
                    placeholder="First Name" 
                    defaultValue={values.player.firstName} 
                    onChange={handleChange('values.player.firstName')} 
                    />
                </FormGroup>
                </Col>
                <Col md={3}>
                <FormGroup>
                    <Input type="lastName" name="lastName" id="lastName" placeholder="Last Name"  />
                </FormGroup>
                </Col>
                <Col md={3}>
                <FormGroup>
                    <Input type="email" name="email" id="email" placeholder="Email" />
                </FormGroup>
                </Col>
            </Row>

            <Row form>
                <Col>
                <Button 
                        label="Back"
                        // primary="true"
                        onClick={this.back}
                        style={{float: 'left'}}>Back</Button>
                </Col>
                <Col>
                <Button 
                        label="Next"
                        // primary="true"
                        onClick={this.continue}
                        style={{float: 'right'}}>Next</Button>
                </Col>
            </Row>

            </Form>
        </Container>

    )
}

}

我无法使用输入值更新播放器属性。另外,我想在此组件中添加多个玩家输入字段。我希望至少要有5名玩家添加到团队中,因此要在父组件中添加玩家数组。请让我知道是否应该以其他方式处理此问题。预先谢谢你!

2 个答案:

答案 0 :(得分:1)

您处于状态的player属性是一个数组,但是您将其视为对象。因此,我假设将有一个玩家,它将是一个对象。这是您的情况的一种解决方案。但是,由于您已经为输入设置了name属性,因此感觉好像您正在处理许多不必要的事情,如传递值等。我不知道,也许您正在考虑其他情况。

这是handleChange部分:

handleChange = input => e => {
  const { target } = e;
  if (input === "player") {
    this.setState(prev => ({
      player: {
        ...prev.player,
        [target.name]: target.value
      }
    }));
  } else {
    this.setState({ [input]: target.value });
  }
};

这是输入部分:

<Input
  type="firstName"
  name="firstName"
  id="firstName"
  placeholder="First Name"
  defaultValue={values.player.firstName}
  onChange={handleChange("player")}
/>

如您所见,我正在传递handleChangeplayer属性,并从您的输入中获取name。然后,在handleChange方法中,使用功能状态和传播语法,我正在更新player

class App extends React.Component {
  state = {
    step: 1,
    teamName: "",
    player: {
      firstName: "",
      lastName: "",
      email: ""
    }
  };

  handleChange = input => e => {
    const { target } = e;
    if (input === "player") {
      this.setState(prev => ({
        player: {
          ...prev.player,
          [target.name]: target.value
        }
      }));
    } else {
      this.setState({ [input]: target.value });
    }
  };

  render() {
    return (
      <div>
        <Input handleChange={this.handleChange} />
        <div>{JSON.stringify(this.state)}</div>
      </div>
    );
  }
}

class Input extends React.Component {
  render() {
    const { handleChange } = this.props;
    return (
      <div>
        <div>
          <span>firstName</span>
          <input
            type="firstName"
            name="firstName"
            id="firstName"
            onChange={handleChange("player")}
          />
        </div>
        <div>
          <span>team</span>
          <input
            type="teamName"
            name="teamName"
            onChange={handleChange("teamName")}
          />
        </div>
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root" />

此处是players的更新版本,为数组(请注意复数名称)。因此,在这个简单的版本中,我们保留了一个players数组,上面有一些唯一的id。这就是我们要锁定更新后的播放器的方式。检查每个input,现在有一个id,我们将其设置为player的那个。在handleChange函数中,这次我们正在映射players(因此将返回一个新数组),然后如果id与我们的玩家的匹配,我们将更新其相关属性。如果id不匹配,我们将不做任何事情返回原始的player

handleChange部分是您需要重点关注的部分。我们正在使用player组件中每个Input的属性来映射和呈现输入。这似乎有点令人困惑,但是如果您深入研究它,便可以理解。我们正在使用Object.entries来映射属性,但是在此之前,我们正在清理id,因为我们不想显示它。

您可以在下面找到有效的代码段。

class App extends React.Component {
  state = {
    step: 1,
    teamName: "",
    players: [
      {
        id: 1,
        firstName: "foo",
        lastName: "foo's lastname",
        email: "foo@foo.com"
      },
      {
        id: 2,
        firstName: "bar",
        lastName: "bar's lastname",
        email: "bar@bar.com"
      }
    ]
  };

  handleChange = input => e => {
    const { target } = e;
    if (input === "players") {
      this.setState({
        players: this.state.players.map(player => {
          if (player.id === Number(target.id)) {
            return { ...player, [target.name]: target.value };
          }

          return player;
        })
      });
    } else {
      this.setState({ [input]: target.value });
    }
  };

  render() {
    return (
      <div>
        <Input handleChange={this.handleChange} players={this.state.players} />
        <div>
          {this.state.players.map(player => (
            <div>
              <h3>Player {player.id}</h3>
              <div>First Name: {player.firstName}</div>
              <div>Last Name: {player.lastName}</div>
              <div>Email: {player.email}</div>
            </div>
          ))}
        </div>
      </div>
    );
  }
}

class Input extends React.Component {
  render() {
    const { handleChange, players } = this.props;
    return (
      <div>
        {players.map(player => {
          const { id, ...playerWithoutId } = player;
          return (
            <div key={player.id}>
              <h3>Player {player.id}</h3>
              {Object.entries(playerWithoutId).map(([key, value]) => (
                <div>
                  <span>{key}</span>
                  <input
                    name={key}
                    id={player.id}
                    onChange={handleChange("players")}
                    defaultValue={value}
                  />
                </div>
              ))}
            </div>
          );
        })}
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root" />

答案 1 :(得分:0)

对于FormPlayerDetails,您不需要传递完整的值,而只需要传递values.players。而且由于您的玩家属性是一个表格,所以请改用map函数