0

I have been trying to create a dynamic from with React. So far I managed to show and hide the form without a problem. I now need to get the values from the created fields and this is causing some problems.

This is my code so far.

First my dynamic component that gets added when people click a button:

class MediaInput extends React.Component {
  render() {
    const linkName = `link-${this.props.index}`;
    const contentName = `content-${this.props.index}`;
    const linkValue = `this.props.${linkName}`;
    const contentValue = `this.props.${contentName}`;

    return (
      <div>
      <ControlLabel>Media (optional)</ControlLabel>
        <input
        name={ linkName }
        value={ linkValue }
        className="form-control"
        placeholder="Add your media url" 
        type="text" 
        />
        <input
        name={ contentName }
        value={ contentValue }
        className="form-control" 
        placeholder="Add your media content"
        type="text" 
        />
    </div>
    );
  }
}

First in the render part I have:

  const mediaFields = this.state.mediaFields.map((Element, index) => {
      return <Element key={ index } index={ index } />
  })  

This is the part of my form where it gets rendered:

 <div>
    { mediaFields }
    <Button onClick={ () => this.add() }>Add media field</Button>
    <Button onClick={ () => this.remove() }>Remove media field</Button>
 </div>

I want to save these values in the state of the parent component:

mediaUrls: { "link-0": null, "link-1": null, "link-2": null}, 
mediaContents: { "content-0": null, "content-1": null, "content-2": null}

It's a bit messy but I was trying to use this example to get it to work: Example

I know that the first part, where I set linkValue and contentValue is not correct, but how should I fix it?

I also know I still need to pass the onChange method to the element but that is the next step.

UPDATE:

I think this would be a cleaner way of doing it but now I have a different problem.

I create an array where I want to store the values. I am able to read them in the child but how do I now save the changes in the parent? How can I read the index of the child in the parent so I can save it in the right place?

class MediaInput extends React.Component {
  render() {
    const linkName = `link${this.props.index}`;
    const contentName = `content${this.props.index}`;

    return (
      <div>
      <ControlLabel>Media (optional)</ControlLabel>
        <input
        onChange={this.props.handleChange}
        name={ linkName }
        value={ this.props.mediaUrls[this.props.index]}
        className="form-control"
        placeholder="Add your media url" 
        type="text" 
        />
        <input
        name={ contentName }
        value={ this.props.mediaContents[this.props.index]}
        className="form-control" 
        placeholder="Add your media content"
        type="text" 
        />
    </div>
    );
  }
}

My state:

      mediaUrls: [ '', '', ''],
      mediaContents: ['', '', ''],

How should my handleChange function look if I want to setState every time the input changes? I want to read the index and change my array based on that.

// Handle media fields 

  handleChange(e){
    const mediaUrls = this.state.mediaUrls;
    // based on the index, change the value in the array


    this.setState({ ??? });
  }
Community
  • 1
  • 1
Deelux
  • 1,045
  • 2
  • 12
  • 26
  • Why do you say that the first part where you set `linkValue` and `contentValue` isn't correct? – 0xcaff Dec 23 '16 at 05:54
  • This article from the React document should answer your question. https://facebook.github.io/react/docs/forms.html – 0xcaff Dec 23 '16 at 05:54
  • Well because when I used linkValue and contentValue it just shows the text as a string instead of actually getting the value from the props. – Deelux Dec 23 '16 at 05:57
  • I have read the documentation. What I am having trouble with is the fact I don't just have one value I need to store. I now manage to read the state of my parent in the child, I made array that should hold all the link values. What I can't figure out is how do I get the index of the child in the parent so I can save the link on a specific location in the array. – Deelux Dec 23 '16 at 06:00
  • `linkValue` and `contentValue` are strings because you define them as such. To get the value of the property, `linkName` use the expression `this.props[linkName]`. [Here's a reference to template literals.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals). Also, you need to pass in your `mediaUrls` and `mediaContents` to the `MediaInput` – 0xcaff Dec 23 '16 at 06:07
  • I have updated the question a little. I have no problems reading the values, I just have problems now saving the new values based on the onChange event..... – Deelux Dec 23 '16 at 06:13
  • Actually your answer was what I was looking for! I still want to learn how to save the value in an array. But for now, just creating 3 different states for my inputs works. Thanks a lot for pointing me to the correct way of writing this.props[linkName] – Deelux Dec 23 '16 at 06:24

1 Answers1

2

To get the index to the parent for updating, simply pass it in the callback.

class MediaInput extends React.Component {
  // ...
  render() {
    return (
      <div>
      <ControlLabel>Media (optional)</ControlLabel>
        <input
        onChange={(event) => this.props.handleChange(event, this.props.index)} />

When dealing state in React, treat it as immutable.

handleChange(e, index) {
    // Shallow copy of array
    const mediaUrls = this.state.mediaUrls.slice();
    mediaUrls[index] = e.target.value;
    this.setState({mediaUrls: mediaUrls});
});
0xcaff
  • 13,085
  • 5
  • 47
  • 55
  • I was wondering, if I would like to instead save both of these values in the same array, so instead of creating two, I want to have just one, how would I be able to do that? – Deelux Dec 23 '16 at 09:03