How to use an array with setState?

2019-07-14 03:59发布

问题:

I am currently using the following to map my array to setState but nothing is getting set and no errors are being logged. If I explicitly write it out line by line, it works. Any thoughts or suggestions on how to go about this?

Set State with Array: (Does not setSate)

const myData = ['message', 'photo', 'isLoggedInhoto'];
const newData = [];

{[...Array(myData.length)].map((x, index) =>
  newData.push(myData[index])
)}

this.setState({
  newData: localStorage.getItem(newData),
})

Line by line (This works and does setState):

this.setState({
  message: localStorage.getItem('message'),
  photo: localStorage.getItem('photo'),
  isLoggedIn: localStorage.getItem('isLoggedIn')
})

回答1:

You can't use an array as key of an object and as parameter of getItem.

You need to loop through newData and use the computed property name syntax:

newData.forEach(key => this.setState({ [key]: localStorage.getItem(key) }))

Or in refactoring your code, you can achieve it with a single loop and a single setState call:

const myData = ['message', 'photo', 'isLoggedInhoto']
const newData = {}

{[...Array(myData.length)].map((x, index) =>
  newData[myData[index]] = localStorage.getItem(myData[index])
)}

 this.setState(newData)


回答2:

Javascript doesn't deconstruct arrays in the way your code assumes they do, unfortunately. You can't set an array as a key for an object and have it expand to all the items in the array. Your code is simply setting a key named newData, not setting an array of keys. Javascript keys in object literals don't have to be quoted. This is using the string "newData", not the variable.

For localStorage.getItem(), the first argument is the string key name. You cannot get multiple keys at once by using an array as the key.

This is a clean way to do it, using ES6 syntax:

this.setState(
    [ 'message', 'photo', 'isLoggedInhoto' ].reduce( ( memo, key ) => ({
        ...memo,
        [ key ]: localStorage.getItem( key )
    }), {} )
);

Explanation:

We're reducing an array, which sounds like what it does. It reduces all items in the array into one thing. The first argument memo is the "accumulator", meaning the thing we're reducing to and building on as we go, and the second item is the current element ('message', 'photo', etc).

An arrow function with this syntax () => ({}) means return an object. Specifically the parenthesis wrapping the curly braces. Without the parenthesis wrapping, as in () => {}, then anything inside the curly braces is interpreted as a regular function body.

This syntax:

{ ...memo }

Means copy the contents of the old object memo into a new object. This is the es6 spread operator.

Finally, to set a key based on a variable name, wrap it in [], as in

{ [ key ]: ... }

So if key is 'message', it will create an object with { message: ... }

The whole code will produce an object like

{
    message: localStorage.getItem('message'),
    photo: localStorage.getItem('photo'),
    isLoggedIn: localStorage.getItem('isLoggedIn')
}

Which is then passed to setState so all keys are set correctly.

Additionally, .map() should not be used in this way in general. .map() is meant to be used to return a new array with the old values mapped to some new values. Discarding the return from .map() means you are using the wrong loop paradigm.

[].forEach() is appropriate for loops that don't return anything and instead have side effects (like pushing to some higher scope array). But in this case neither is ideal, .reduce() is more semantically correct to produce an object based on an array.



回答3:

You are trying to create a new state object, with properties from a list, and data extracted from localStorage using the list as keys.

  1. Iterate the list using Array#map
  2. On each iteration create a new object, with a key from the list, and get the data from localStorage. Don't forget to JSON.parse() if you've stored a stringified JSON data,
  3. Spread the array and create a new object using Object#assign.
  4. Set resulting object to your state.

The code:

const myData = ['message', 'photo', 'isLoggedInhoto'];
const state = Object.assign({}, ...myData.map((prop) => ({
  [prop]: localStorage.getItem(prop) // or JSON.parse(localStorage.getItem(prop)) if your storing a JSON
})));

this.setState(state);