3

My question is almost the same as this one.

In this case, the person has a map of states created in a hard coded way:

const App = props => {
  const [state, changeState] = useState({
    name: "",
    eventTitle: "",
    details: "",
    list: "",
    toggleIndex: "",
    editName: "",
    editEventTitle: "",
    editDetails: "",
  });

The difference is that I want to create those states dynamically, receiving them from another component. I tried something like this but it obviously did not work:

const App = props => {
  const [state, changeState] = useState({
    props.inputs.map((input, i) =>
      input = ""
    )
  });

Do you know any solution for this?

6
  • What does props.inputs look like? Commented Mar 16, 2020 at 17:43
  • @NicholasTower it has a list of input names that I want to have a state attached to them Commented Mar 16, 2020 at 17:55
  • { "tag": "1231", "description": "Lorem Ipsum", "inputs": [ "Field1", "Field2", "Field3", "Field4", ] }; Commented Mar 16, 2020 at 17:56
  • 1
    You'd probably need to reduce the props.inputs into a new object. Commented Mar 16, 2020 at 17:57
  • 2
    See Christos' answer, it's exactly what I meant. Commented Mar 16, 2020 at 20:07

3 Answers 3

3

Assuming props.input is just an array of the keys you want, you can populate the state object by iterating through the array and dynamically assigning the state object key with []

const [state, changeState] = useState({});

useEffect(() => {
    props.input.forEach(item => changeState(prevState => ({...prevState, [item]: "" })));
}, [props.input])
Sign up to request clarification or add additional context in comments.

4 Comments

This is not working for me, {...prevState give me declaration or statement expected and , [item]: "" }); gives me ',' expected. How does this code works?
Sorry I missed the inner () (now added in the edit). Because you can access object keys by variables through the use of [] (ie. obj[variable]), you can leverage this to also assign them dynamically when you iterate through the array. The callback just lets you use the ... spread syntax to include the rest of the keys that already exist in changeState but are not explicitly defined each time you modify it to add the next element: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
I still have ')' expected in the end of ({...prevState, [item]: "" }));, in the semicolon. And I understand the forEach part but what does this part do , [props.input])?
Oops I forgot to close the forEach (fixed). In this case I am triggering this to take place when the component mounts via useEffect (reactjs.org/docs/hooks-effect.html), then specifying [props.input] as its second parameter to signify that it also occurs when props.input is updated by the parent component as well. I just assumed that this was an intended behavior but you can choose to modify it to however you like (ie. only once on mount and that's it).
2

You can do this by using the props.inputs directly to the state combine it with reduce to create a new object from the inputs array:

const App = props => {
  const [state, changeState] = useState(
    props.inputs.reduce((acc, cur, arr) => ({
      [cur]: "",
      ...acc
    }), {})
  );

  return (
    <ul>
      {Object.entries(state).map(([name, value], index) => (
        <li key={`li-${index}`}><strong>{name}</strong>: {`"${value}"`}</li>
      ))}
    </ul>
  );
}

If we use it like this:

<App inputs={['name', 'details', 'list', 'editName']}/>

We'll have a result like this:

editName: ""
list: ""
details: ""
name: ""

You can check the working Stackblitz here.

5 Comments

Would appreciate to see the argument on why this is downvoted. useState has access to component props when it's initialized, so this will work for dynamic props passed when the component is created. We can of course use the useEffect hook if we have more dynamic props that need to be changed on renders.
I did not downvoted this as I appreciate any help. Despite this, I tried this suggestion and it just create a state with a list of the names on the inputs and not a map like structure that will hold the name and the value of the input fields
props.inputs is an array.
@EmileBergeron I misunderstood the question and didn't realize props.inputs is an array we need to use the elements for keys.
@PedroVieira you can do what you want using reduce. Please check my updated answer.
0

in my case I was trying to get an item unique ID when click on mapped items, and opening a dialog to confirm selected item to be deleted, as shown below

export const StatsTable = (props) => {
  const [itemUid, setItemUid] = useState('')
  const [open, setOpen] = useState(false)

  const handleClickOpen = () => {
    setOpen(true)
  }

  console.log('====================================')
  console.log(itemUid)
  console.log('====================================')
...
 return (
...
  {items.map((item) => {
...
    <Button
      type="submit"
      onClick={() =>
        handleClickOpen(setItemUid(item.uid))
      }
     >
       Delete
     </Button>
   }
}

...

Image Example

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.