动态添加表单字段 -- ReactJs

3
我的需求是动态添加表单字段。我能够在点击+添加标题按钮时动态添加表单组。
在该表单组中,我有一个+添加内容按钮,当点击后应该向组中添加一个新的输入元素。
我能够相应地修改JSON并记录,并更新状态,但我的视图未按预期呈现。
任何帮助都将受到高度赞赏。
Stackblitz: https://stackblitz.com/edit/react-utjwsu?embed=1&file=index.js / 相应的代码 /
import React, { Component } from 'react';
import { render } from 'react-dom';
import { Form, FormGroup, Label, Progress,Button } from 'reactstrap';
import Hello from './Hello';
import './style.css';


const byPropKey = (propertyName, value) => () => ({
    [propertyName]: value,
  });
class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      name: 'React',
      Information:'',
      ImageUrl:'',
      isUploading: false,
      isUploaded: true,
      showProgress: false,
      progress: 0,
      avatarUrl:'',
      ref:'WhitePapersItems',
      formValid: false,
      content: [{Heading: "", Content: [{value:""}]}]
    };

        this.AddContentBox = this.AddContentBox.bind(this);
        this.handleChange = this.handleChange.bind(this);
        this.AddContentInput = this.AddContentInput.bind(this);
  }

  AddContentBox(){
        return this.state.content.map((element,i)=>(
            <div className="header-content" key={i} >
                <div className="heading-content-wrapper">
                <FormGroup>
                <Label className="set-label-pos upload-img" for="Heading">Heading</Label>
                <input className="form-control" onChange={this.handleChange.bind(this, i)}
                    type="text"/>
                </FormGroup>
                <FormGroup>
                <Label className="set-label-pos upload-img" for="Heading">Content</Label>
                <input className="form-control" onChange={this.handleChange.bind(this, i)}
                    type="text"/>
                </FormGroup>
                {/* {this.AddContentInput} */}
                <FormGroup>
                <Button color="success" onClick={this.AddContentInput.bind(this,i)}>+ Add Content</Button>
                </FormGroup>
            </div>
            </div>
        ))
    }

    handleChange(i, e) {
    const { Heading, value } = e.target;
      let content = [...this.state.content];
      content[i] = {...content[i], [Heading]: value};
      this.setState({ content });
   }

   AddContentInput(index, val){
        console.log('Add ContentInput triggered',val,index);       
        const contents = this.state.content;
        contents[index].Content.push({value:''});
        this.setState({content:contents});
        const item = this.state.content[index].Content;
        console.log('item', item)
        return item.map((element, i)=>(
            <FormGroup>
               <Label className="set-label-pos upload-img" for="Heading">Content</Label>
               <input className="form-control" type="text"/>
           </FormGroup>
       ))

        // console.log(this.state)
        // this.addContentField(index);
    }

     addHeading(event){
        event.preventDefault();
        this.setState(prevState => ({ 
            content: [...prevState.content, { Heading: "", Content: [{value:""}] }]
        }))
        this.AddContentBox();
   }

  render() {
    return (
      <div>


        <Form onSubmit={this.onSubmit}>

                <FormGroup>
                    <Label className="set-label-pos upload-img" for="Information">Information</Label>
                    <textarea className="form-control" rows="10" onChange={event => this.setState(byPropKey('Information', event.target.value))}
                        type="text"/>
                </FormGroup>
                {this.AddContentBox()}
                <div className="add-heading-button">
                    <Button color="primary" onClick={this.addHeading.bind(this)}>+ Add Heading</Button>
                </div>
                <FormGroup>
                <input type="hidden" value={this.state.ImageUrl} onChange={event => this.setState(byPropKey('ImageUrl', event.target.value))}/>
                </FormGroup>

                <Button className="btn btn-danger" size="lg" type="submit">+ Add White Papers Item</Button>
                </Form>

      </div>
    );
  }
}

render(<App />, document.getElementById('root'));
2个回答

2
对于那些可能遇到这种情况的人,我来回答自己的问题。
更新我的 stackblitz:https://stackblitz.com/edit/react-utjwsu?embed=1&file=index.js 我尝试渲染一个字段而不是渲染所有内容字段。
/ 代码放在这里 /
import React, { Component } from 'react';
import { render } from 'react-dom';
import { Form, FormGroup, Label, Progress,Button } from 'reactstrap';
import Hello from './Hello';
import './style.css';


const byPropKey = (propertyName, value) => () => ({
    [propertyName]: value,
  });
class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      name: 'React',
      Information:'',
      ImageUrl:'',
      isUploading: false,
      isUploaded: true,
      showProgress: false,
      progress: 0,
      avatarUrl:'',
      ref:'WhitePapersItems',
      formValid: false,
      content: [{Heading: "", Content: [{value:""}]}]
    };

        this.AddContentBox = this.AddContentBox.bind(this);
        this.AddContentInput = this.AddContentInput.bind(this);
        //this.addContentTextBox = this.addContentTextBox.bind(this);
  }

  AddContentInputFields(element) {
    return element.Content.map(content => {
      return (
        <FormGroup>
          <Label className="set-label-pos upload-img" for="Heading">Content</Label>
          <input className="form-control" type="text"/>
        </FormGroup>
      );
    })
  }

  AddContentBox(){
        return this.state.content.map((element,i)=>(
            <div className="header-content" key={i} >
                <div className="heading-content-wrapper">
                <FormGroup>
                <Label className="set-label-pos upload-img" for="Heading">Heading</Label>
                <input className="form-control" type="text"/>
                </FormGroup>
                { this.AddContentInputFields(element) }
                <FormGroup>
                <Button color="success" onClick={this.AddContentInput.bind(this,i)}>+ Add Content</Button>
                </FormGroup>
            </div>
            </div>
        ))
    }

    handleChange(i, e) {
    const { Heading, value } = e.target;
      let content = [...this.state.content];
      content[i] = {...content[i], [Heading]: value};
      this.setState({ content });
   }

   AddContentInput(index, val){
        console.log('Add ContentInput triggered',val,index);       
        const contents = _.cloneDeep(this.state.content);
        contents[index].Content.push({value:''});

        const item = contents[index].Content;
        console.log('item', item);
        this.setState({content:contents});
        //this.addContentTextBox(item);

    }

    // addContentTextBox(item){
    //   return item.map((element, i)=>(
    //         <FormGroup>
    //            <Label className="set-label-pos upload-img" for="Heading">Content</Label>
    //            <input className="form-control" type="text"/>
    //        </FormGroup>
    //    ));
    // }

     addHeading(event){
        event.preventDefault();
        this.setState(prevState => ({ 
            content: [...prevState.content, { Heading: "", Content: [{value:""}] }]
        }))
        this.AddContentBox();
   }

  render() {
    return (
      <div>


        <Form onSubmit={this.onSubmit}>

                <FormGroup>
                    <Label className="set-label-pos upload-img" for="Information">Information</Label>
                    <textarea className="form-control" rows="10" onChange={event => this.setState(byPropKey('Information', event.target.value))}
                        type="text"/>
                </FormGroup>
                {this.AddContentBox()}
                <div className="add-heading-button">
                    <Button color="primary" onClick={this.addHeading.bind(this)}>+ Add Heading</Button>
                </div>
                <FormGroup>
                <input type="hidden" value={this.state.ImageUrl} onChange={event => this.setState(byPropKey('ImageUrl', event.target.value))}/>
                </FormGroup>

                <Button className="btn btn-danger" size="lg" type="submit">+ Add White Papers Item</Button>
                </Form>

      </div>
    );
  }
}

render(<App />, document.getElementById('root'));

-1

这个问题出现的原因是因为你直接修改了this.state。React不会自动重新渲染,因为你没有告诉它状态已经改变。

你需要调用this.setState()来设置新的状态。

如果你正在添加一个对象(比如this.state.content),你需要确保你复制了这个对象(使用像_.cloneDeep()这样的东西)以确保你没有修改现有的对象。

希望这足够解释让你能够解决你的问题。

更新: 你正在间接地修改状态,使用以下代码: const contents = this.state.content; contents[index].Content.push({value:''});

contents是一个变量,它不是this.state.content的副本,而是对它的引用。所以向对象中推入一个值实际上是直接修改状态。对于我在原始答案中没有表述清楚,我深感抱歉。


1
我没有直接修改状态的地方。 - Amit
试一下这个 - 不确定是否完全符合您的意图... https://stackblitz.com/edit/react-9sesua?file=index.js - Mikkel
当点击“添加内容”按钮时,我只想再增加一个输入框,即一个标题和两个内容框。 - Amit
1
请告诉我是否仍需要帮助。 - Mikkel
是的,我仍然在为同样的问题苦苦挣扎。 - Amit
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接