Ycon
Ycon

Reputation: 1950

Editing immutable state items

I'm trying to update some immutable data. However, I am doing it incorrectly and it doesn't work.

My understanding, is I need to pass it a unique it (eg the key) so it knows which item in state to update. But this may be wrong, and I would like to do this the proper way. I tried the immutability helper, but had no success.

I will also want to update/add/remove nested items in an immutable array.

I put this in a codeSandbox for ease https://codesandbox.io/s/mz5WnOXn

class App extends React.Component {
  ...
  editTitle = (e,changeKey) => {
   this.setState({
    movies: update(this.state.movies, {changeKey: {name: {$set: e.target.value}}})
  })
    console.log('Edit title ran on', key)
  };

  ...

  render() {
  ...
    return (
      <div>
        {movies.map(e => {
          return (
            <div>
              <input key={e.pk} onChange={this.editTitle} value={e.name} changeKey={e.pk} type="text" />
              With genres:
              {e.genres.map(x => {
                return (
                  <span key={x.pk}>
                    <input onChange={this.editGenres} value={x.name} changeKey={x.pk} type="text" />
                  </span>
                );
              })}
            </div>
          );
        })}
      </div>
    );
  }
}

Upvotes: 1

Views: 56

Answers (2)

Sagiv b.g
Sagiv b.g

Reputation: 31024

Couple of things,
1. By looking at your data i don't see any pk property maybe its a typo (are you trying to use the id property instead?).
2. The key prop should be on the root element that you return on each iteration of your map function.
3. You can't just add none native attributes to a native Html element instead you can use the data-* like data-changeKey and access it via e.target.getAttribute('data-changeKey')

Something like this:

import { render } from 'react-dom';
import React from 'react';
import { reject, pluck, prop, pipe, filter, flatten, uniqBy } from 'ramda';
import { smallData } from './components/data.js';
import './index.css';
import Result from './components/Result';
import update from 'react-addons-update';

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      movies: smallData.movies,
      selectedFilters: {},
      searchQuery: '',
    };
  }
  editTitle = (e) => {
    const attr = e.target.getAttribute('data-changeKey');
    this.setState({
      movies: update(this.state.movies, {
        // what are you trying to do here?
        //changeKey: { name: { $set: e.target.value } },
      }),
    });
    console.log('edit title ran on', attr);
  };

  onSearch = e => {
    this.setState({
      searchQuery: e.target.value,
    });
  };

  render() {
    const { movies } = this.state;
    return (
      <div>
        {movies.map((e, i) => {
          return (
            <div key={i}>
              <input
                onChange={this.editTitle}
                value={e.name}
                data-changeKey={e.id}
                type="text"
              />
              With genres:
              {e.genres.map((x,y) => {
                return (
                  <span key={y}>
                    <input
                      onChange={this.editTitle}
                      value={x.name}
                      data-changeKey={x.id}
                      type="text"
                    />
                  </span>
                );
              })}
              Add new genre:
              <input type="text" />
            </div>
          );
        })}
      </div>
    );
  }
}

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

Upvotes: 1

Ycon
Ycon

Reputation: 1950

Index is what update needs in order to know which data to change.

Firstly, map sure your map is creating an index:

{movies.map((e,index) => {
return (
            <input key={e.pk} onChange={e => this.editTitle(e, index)} value={e.name} >
...

Then pass the index to the immutability-helper update like so: to:

  editTitle = (e, index) => {
   this.setState({
    movies: update(this.state.movies, {[index]: {name: {$set: e.target.value}}})
  })

Upvotes: 0

Related Questions