0

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

更新了更多代码。我正在尝试构建一个多步骤表单。第一页是球队信息,第二页是球员信息。

父组件:

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 名玩家添加到团队中,因此父组件中的玩家数组。请让我知道我是否应该以另一种方式解决这个问题。先感谢您!

4

2 回答 2

2

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")}
/>

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

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的 's。这就是我们将如何定位更新的播放器。检查每个input,有一个idnow ,我们将其设置为player's 。在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" />

于 2020-05-11T04:54:33.327 回答
0

对于 FormPlayerDetails,您不需要传递完整的值,而只需传递 values.players。而且由于您的玩家属性是一张桌子……请改用地图功能

于 2020-05-11T04:36:57.297 回答