Deelux
Deelux

Reputation: 1195

What is the best way to save my dynamicly created data using React and Javascript?

I have been trying to get my dynamic form to work but I was wondering what the best practice is to save data generated like this.

My solution works but I have the feeling there is a better way of doing it.

At the moment I am saving both of the values of the input fields in a seperate array. But actually they belong together, so I have a link value and a content value that I need to save.

Later I need to map over these values to create new elements with the stored value.

The problem I have now is that I don't know how to map over two arrays at the same time. I am also wondering if it's not better to just create one Object with all of these values instead and then just map over that.

Hope someone can help me out.

This is my code:

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={(event) => this.props.handleChangeUrl(event, this.props.index)}
        name={ linkName }
        value={ this.props.mediaUrls[this.props.index]}
        className="form-control"
        placeholder="Add your media url. We accept YouTube, Vimeo and SoundCloud links" 
        type="text" 
        />
        <input
        name={ contentName }
        onChange={(event) => this.props.handleChangeContent(event, this.props.index)}
        value={ this.props.mediaContents[this.props.index]}
        className="form-control" 
        placeholder="Add your media content"
        type="text" 
        />
    </div>
    );
  }
}

export default class AddSparkShanghai extends Component {
  constructor(props) {
    super(props);

    this.createSpark = this.createSpark.bind(this);
    this.onChange = this.onChange.bind(this);
    this.handleChangeUrl = this.handleChangeUrl.bind(this);
    this.handleChangeContent = this.handleChangeContent.bind(this);


    this.state ={
      mediaFields: [],
      content: [],
      mediaUrls: [ null, null, null ],
      mediaContents: [ {'', '', ''],
    };
  }

[...]

// Add/remove media fields 

  add() {
    event.preventDefault();
    const mediaFields = this.state.mediaFields.concat(MediaInput);
    if (i < 3) {
      this.setState({ mediaFields });
      i++
    } else {
      Bert.alert('Only 3 media links are allowed', 'danger');
    }
  }

  remove() {
    event.preventDefault();
    const lastElement = this.state.mediaFields.pop();
    const mediaFields = this.state.mediaFields;
    this.setState({ mediaFields });
    i--
  }

// Handle change media fields 

  handleChangeUrl(e, index) {
    // Shallow copy of array
    const mediaUrls = this.state.mediaUrls.slice();
    let url = e.target.value
    if (!/^https?:\/\//i.test(url)) {
      url = 'http://' + url;
    }

    mediaUrls[index] = url;
      this.setState({ mediaUrls});
  }

  handleChangeContent(e, index) {
    // Shallow copy of array
    const mediaContents = this.state.mediaContents.slice();
    mediaContents[index] = e.target.value;
      this.setState({ mediaContents });
  }

[...]

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

[...]

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

Upvotes: 3

Views: 280

Answers (2)

Deelux
Deelux

Reputation: 1195

With the help of JosephGarrone I was able to get this example working like I want!

Here is the full code:

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={(event) => this.props.handleChangeUrl(event, this.props.index)}
        name={ linkName }
        value={ this.props.mediaData[this.props.index].link}
        className="form-control"
        placeholder="Add your media url. We accept YouTube, Vimeo and SoundCloud links" 
        type="text" 
        />
        <input
        name={ contentName }
        onChange={(event) => this.props.handleChangeContent(event, this.props.index)}
        value={ this.props.mediaData[this.props.index].content}
        className="form-control" 
        placeholder="Add your media content"
        type="text" 
        />
    </div>
    );
  }
}

export default class AddSparkShanghai extends Component {
  constructor(props) {
    super(props);

    this.handleChangeUrl = this.handleChangeUrl.bind(this);
    this.handleChangeContent = this.handleChangeContent.bind(this);


    this.state ={
      mediaFields: [],
      mediaData: [],
    };
  }

[...]

  // Add/remove media fields 

add() {
  event.preventDefault();
  const mediaFields = this.state.mediaFields.concat(MediaInput);
  const mediaData = this.state.mediaData.slice();
  mediaData.push({ link: "", content: "" });
  if (i < 3) {
    this.setState({ mediaData, mediaFields });
    i++
  } else {
    Bert.alert('Only 3 media links are allowed', 'danger');
  }
}

remove() {
  event.preventDefault();
  const lastElement = this.state.mediaFields.pop();
  const mediaFields = this.state.mediaFields;
  const mediaData = this.state.mediaData.slice();
  mediaData.pop();
  this.setState({ mediaData, mediaFields });
  i--;
}

// Handle change media fields 

handleChangeUrl(e, index) {
  // Shallow copy of array
  const tempData = this.state.mediaData.slice();
  let url = e.target.value
  if (!/^https?:\/\//i.test(url)) {
    url = 'http://' + url;
  }

  tempData[index].link = url;
  this.setState({ mediaData: tempData });
  console.log(this.state.mediaData)
}

handleChangeContent(e, index) {
  // Shallow copy of array
  const tempData = this.state.mediaData.slice();
  tempData[index].content = e.target.value;
  this.setState({ mediaData: tempData });
  console.log(this.state.mediaData)
}

[...]

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

[...]

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

Upvotes: 1

JosephGarrone
JosephGarrone

Reputation: 4161

Untested code, but it should work if you change the following:

Change this:

this.state ={
  mediaFields: [],
  content: [],
  mediaUrls: [ null, null, null ],
  mediaContents: [ {'', '', ''],
};

To:

this.state ={
  mediaFields: [],
  content: [],
  data: [],
};

And change:

handleChangeUrl(e, index) {
  // Shallow copy of array
  const mediaUrls = this.state.mediaUrls.slice();
  let url = e.target.value
  if (!/^https?:\/\//i.test(url)) {
    url = 'http://' + url;
  }

   mediaUrls[index] = url;
   this.setState({ mediaUrls});
}

To:

handleChangeUrl(e, index) {
  // Shallow copy of array
  const tempData = this.state.data.slice();
  let url = e.target.value
  if (!/^https?:\/\//i.test(url)) {
    url = 'http://' + url;
  }

  tempData[index].link = url;
  this.setState({ tempData });
}

And change:

handleChangeContent(e, index) {
  // Shallow copy of array
  const mediaContents = this.state.mediaContents.slice();
  mediaContents[index] = e.target.value;
  this.setState({ mediaContents });
}

To:

handleChangeContent(e, index) {
  // Shallow copy of array
  const tempData = this.state.data.slice();
  tempData[index].content = e.target.value;
  this.setState({ tempData });
}

And lastly:

add() {
  event.preventDefault();
  const mediaFields = this.state.mediaFields.concat(MediaInput);
  if (i < 3) {
    this.setState({ mediaFields });
    i++
  } else {
    Bert.alert('Only 3 media links are allowed', 'danger');
  }
}

remove() {
  event.preventDefault();
  const lastElement = this.state.mediaFields.pop();
  const mediaFields = this.state.mediaFields;
  this.setState({ mediaFields });
  i--;
}

To:

add() {
  event.preventDefault();
  const mediaFields = this.state.mediaFields.concat(MediaInput);
  const data = this.state.data.slice();
  data.push({ link: "", content: "" });
  if (i < 3) {
    this.setState({ data, mediaFields });
    i++
  } else {
    Bert.alert('Only 3 media links are allowed', 'danger');
  }
}

remove() {
  event.preventDefault();
  const lastElement = this.state.mediaFields.pop();
  const mediaFields = this.state.mediaFields;
  const data = this.state.data.slice();
  data.pop();
  this.setState({ data, mediaFields });
  i--;
}

NOTE: Has been edited to fix the last lot of changes!

Upvotes: 1

Related Questions