动态添加表单字段-ReactJs

时间:2018-12-30 10:38:55

标签: javascript reactjs

我的要求是动态添加表单字段。点击按钮 +添加标题,我可以动态添加表单组。

在该表单组中,我有一个按钮 +添加内容,单击该按钮时应将新的输入元素添加到该组中。

我能够相应地修改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 个答案:

答案 0 :(得分: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 :(得分:-1)

之所以无法正常工作,是因为您正在直接修改this.state。 React不会自动重新渲染,因为您没有告诉它状态已经改变。

您需要致电this.setState()来设置新状态。

如果要添加到对象(例如this.state.content,则需要确保获取该对象的副本(使用类似_.cloneDeep()的对象),以确保您未修改现有对象。

希望这对您来说足以解决问题。

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

contents是一个变量,不是this.state.content的副本,而是对其的引用。因此,将值推入对象实际上是直接修改状态。我很抱歉没有在原始答案中对此加以澄清