Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React useState does not seem to accept a default value here?

I have a chart component in which specific lines can be shown/hidden. To keep track of which lines are active I keep an activeKeys array in a state. Initially I get the key names from a function getKeys that takes the array of data.

When I do this:

    const defaultValue = getKeys(data)
    console.log('defaultValue from keys', defaultValue)
    const [activeKeys, setActiveKeys] = useState(defaultValue)
    console.log('activeKeys', activeKeys)

First console log shows the correct keys:

["createdCount", "confirmedCount", "hasFeedbackCount"]

Second console log shows []

But if I do:

const defaultValue = ["createdCount", "confirmedCount","hasFeedbackCount"]
console.log('defaultValue', defaultValue)
const [activeKeys, setActiveKeys] = useState(defaultValue)
console.log('activeKeys', activeKeys)

First console log shows the same array:

["createdCount", "confirmedCount", "hasFeedbackCount"]

And activeKeys shows the correct array:

["createdCount", "confirmedCount", "hasFeedbackCount"]

Is useState broken or something? By the getKeys is a simple function, no promise or anything like that. It looks like this:

const getKeys = (data: Props['data']): string[] => {
  const reduced = data.reduce((acc, datum) => [...acc, ...Object.keys(datum.lines)] as any, [])
  const setted = new Set(reduced)
  const arrayed = Array.from(setted)
  return arrayed
}

The shape of Props['data'] is:

  data: {
    date: string
    lines: Partial<Record<string, number>>
  }[]
like image 551
Mehdi Saffar Avatar asked Jun 24 '19 07:06

Mehdi Saffar


2 Answers

I had this same problem. It is correct that there is not a bug with React.useState.

The issue here is that your component is being mounted and calculating the empty array as the default value. Subsequently your "default value" is changing to your expected default value but the React.useState already has a default value of empty array.

To fix this you'll need to avoid mounting the component until the desired default value is available. Alternatively you could probably do something like using a React.useEffect hook to look for a change in props and call the setter (second item in the array) of the React.useState output.

like image 50
Andrue Anderson Avatar answered Oct 23 '22 11:10

Andrue Anderson


There doesn't seem to be a problem with useState.

As per React DOCs:

What do we pass to useState as an argument?

The only argument to the useState() Hook is the initial state. Unlike with classes, the state doesn’t have to be an object. We can keep a number or a string if that’s all we need. In our example, we just want a number for how many times the user clicked, so pass 0 as initial state for our variable. (If we wanted to store two different values in state, we would call useState() twice.)

So it's perfectly normal to pass an array of strings as initial state for useState. Even if it's the result of a function. (see snippet below).

I couldn't reproduce your getKeys function here on SO Snippet Editor, I suggest you further inspect that. Although this could also be a problem with the snippet builder that may not support some JS features.

NOTE: This is not a problem with useState. Something else is going wrong. Try to submit a working example of the issue being reproduced.

function App() {
  
  const someFunction = (x) => {
    return x;
  }
  
  const data = ['A','B','C'];
  
  const [state,setState] = React.useState(someFunction(data));
  
  console.log(state);
  
  return(
    <div>{state.toString()}</div>
  );
}

ReactDOM.render(<App/>,document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"/>
like image 21
cbdeveloper Avatar answered Oct 23 '22 11:10

cbdeveloper