0

I have a state array that looks similar to this

this.state = {
  var: "",
  arr1: [],
  other: ["item1", "item2", "item3"]
}

In my render() I list out the contents of the "other" array, and next to it, a user can put any number, which would be their ranking (doesn't matter if repeated) of the items in the "other array as shown below

<Row className="App">
  <ul>
    {this.state.other.map((item, index) =>
      <li key={index}>{item}<Input className="rankCols" type="number" id="ranking" onChange={this.handleChange.bind(this, item)} /></li>
    )}
  </ul>
</Row>

Then in a handleChange function, I store the user ranking in an array of objects along with the pName

handleChange = (item, event) => {
  const value = event.target.value
  this.setState(prev => {
    const idx = prev.arr1.findIndex(obj => obj.pName === item)
    if (idx > -1) {
      return {
        arr1: [...prev.arr1.slice(0, idx), {
          pName: item,
          priority: value
        }, ...prev.arr1.slice(0, idx + 1)]
      }
    } else {
      return {
        arr1: prev.arr1.concat([{
          pName: item,
          priority: value
        }])
      }
    }

  });
}

Now the problem is that, instead of the arr1 looking like

arr1: [{
    pName: "item1",
    priority: "3"
  },
  {
    pName: "item2",
    priority: "1"
  },
  {
    pName: "item3",
    priority: "2"
  }
]

It repeats the some of the values so it looks more like

arr1: [{
    pName: "item1",
    priority: "3"
  },
  {
    pName: "item2",
    priority: "1"
  },
  {
    pName: "item3",
    priority: "2"
  },
  {
    pName: "item1",
    priority: "3"
  },
  {
    pName: "item2",
    priority: "1"
  },
  {
    pName: "item3",
    priority: "2"
  }
]

So it gives me problems in the backend, because each item cannot exist more than once. How could I prevent them from repeating?

Mosh Feu
  • 28,354
  • 16
  • 88
  • 135

2 Answers2

0

You're not slicing array properly, in the second slice you're using ...prev.arr1.slice(0, idx + 1) which should be ...prev.arr1.slice(idx + 1,)

you need to change this

if(idx > -1) {
           return {
              arr1: [...prev.arr1.slice(0, idx), {pName: item, priority: value}, ...prev.arr1.slice(0, idx + 1)]
           }
      }

to this

if(idx > -1) {
           return {
              arr1: [...prev.arr1.slice(0, idx), {pName: item, priority: value}, ...prev.arr1.slice(idx + 1,)]
           }
      }
Code Maniac
  • 37,143
  • 5
  • 39
  • 60
0

The issue, as CodeManiac points out, is that you're calling slice with incorrect boundaries.

However, doing an immutable update on an array is something you'll need to repeat anywhere you have an array in state. Break this up into a separate function, insert, so you don't have to repeat yourself. It also helps isolate complexity and keep your handlers clean -

const insert = (a = [], pos = 0, v = null) =>
  pos === -1
    ? [ ...a, v ]
    : [ ...a.slice(0, pos), v, ...a.slice(pos + 1) ]

Then in your component -

handleChange = (pName, event) =>
{ const priority = event.target.value
  const arr1 = this.state.arr1
  const next =
    insert
      ( arr1
      , arr1.findIndex(o => o.pName === pName)
      , { pName, priority }
      )

  this.setState({ arr1: next })
}

You might also consider currying your handler -

handlerChange = pName => event =>
{ // ...
}

So in your render, you can use it like this -

<Row className="App">
  <ul>
    {this.state.other.map((item, index) =>
      <li key={index}>{item}<Input className="rankCols" type="number" id="ranking" onChange={this.handleChange(item)} /></li>
    )}
  </ul>
</Row>

Below is a fully functioning demo -

const insert = (a = [], pos = 0, v = null) =>
  pos === -1
    ? [ ...a, v ]
    : [ ...a.slice(0, pos), v, ...a.slice(pos + 1) ]

class Main extends React.Component {

  state =
    { priorities: [] 
    , items: ["a", "b", "c" ]
    }

  click = name => event =>
  { const priority = event.target.value
    const arr = this.state.priorities
    const next =
      insert
        ( arr
        , arr.findIndex(o => o.name === name)
        , { name, priority }
        )

    this.setState({ priorities: next })
  }

  getPriority = name =>
  { const empty = { priority: 0 }
    const p = this.state.priorities.find(p => p.name === name) || empty
    return JSON.stringify(p)
  }

  render() {
    return (
      <ul>
        {this.state.items.map((name, key) =>
          <li key={key}>
            <span>{name}</span>
            <button value="1" onClick={this.click(name)}>1</button>
            <button value="2" onClick={this.click(name)}>2</button>
            <button value="3" onClick={this.click(name)}>3</button>
            <input disabled value={this.getPriority(name)} />
          </li>
        )}
      </ul>
    )
  }
}

ReactDOM.render(<Main />, document.querySelector("main"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<main></main>
Mulan
  • 129,518
  • 31
  • 228
  • 259