{"id":3759,"date":"2020-09-28T17:38:55","date_gmt":"2020-09-28T23:38:55","guid":{"rendered":"http:\/\/benincosa.com\/?p=3759"},"modified":"2020-09-28T17:38:55","modified_gmt":"2020-09-28T23:38:55","slug":"react-hooks-usestate-with-arrays","status":"publish","type":"post","link":"https:\/\/benincosa.com\/?p=3759","title":{"rendered":"React Hooks: useState with Arrays"},"content":{"rendered":"\n<p>I spent a good portion of the work day today trying to code up a simple form to accept a list of zip codes and put them in an array.  <\/p>\n\n\n\n<p>The final form looks as follows: <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" loading=\"lazy\" width=\"2718\" height=\"656\" src=\"https:\/\/benincosa.com\/wp-content\/uploads\/2020\/09\/Screen-Shot-2020-09-28-at-4.23.48-PM.png\" alt=\"\" class=\"wp-image-3760\"\/><\/figure>\n\n\n\n<p>As you can see there are a few zip codes and then we can click the &#8216;Add Zip Code&#8217; button and we get a new set of inputs. <\/p>\n\n\n\n<p>To make this work, our form code looks as follows: <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import React, {useState} from 'react';\nimport {\n  Button,\n  ButtonGroup,\n  Col,\n  Form,\n  FormGroup,\n  FormText,\n  Input,\n  Label,\n  Row,\n} from 'reactstrap';\nconst MyForm = ({formSubmitFunc}) => {\n  const &#91;zipCodes, setZipCodes] = useState(&#91;\"\"]);\n  const &#91;costs, setCosts] = useState(&#91;\"\"]);\n\n  return(\n    &lt;Form className=\"form\" onSubmit={formSubmitFunc}>\n    { Array.apply(null, {length: zipCodes.length}).map( (row, index) =>\n      &lt;Row key={index}>\n        &lt;Col>\n          {console.log(\"row: \", row, \" index: \", index, \"value: \", zipCodes&#91;index] )}\n          &lt;FormGroup>\n            &lt;Label>Zip Codes&lt;\/Label>\n            &lt;Input\n              type=\"input\"\n              value={zipCodes&#91;index]}\n              onChange={e => {setZipCode(index, e.target.value)}}\/>\n            &lt;FormText>What Zip Codes will you service?&lt;\/FormText>\n          &lt;\/FormGroup>\n        &lt;\/Col>\n        &lt;Col>\n          &lt;FormGroup>\n            &lt;Label>Cost for service in this Zip Code&lt;\/Label>\n            &lt;Input\n              type=\"input\"\n              value={costs&#91;index]}\n              onChange={(e) => setCost(index, e.target.value)}\/>\n            &lt;FormText>How much will you charge in this Zip Code for your service?&lt;\/FormText>\n          &lt;\/FormGroup>\n        &lt;\/Col>\n        &lt;Col className=\"align-self-center\">\n        { index === (zipCodes.length - 1) ?\n            &lt;ButtonGroup>\n              &lt;Button color=\"primary\" onClick={addRow}>Add Zip Code&lt;\/Button>\n              {index !== 0 &amp;&amp;\n                &lt;>\n                  &amp;nbsp;\n                  &lt;Button color=\"secondary\" onClick={removeRow}>Remove Zip Code&lt;\/Button>\n                &lt;\/>\n              }\n            &lt;\/ButtonGroup>\n            :\n            &lt;div>&amp;nbsp;&lt;\/div>\n          }\n        &lt;\/Col>\n      &lt;\/Row>\n    )}\n    &lt;\/Form>\n  );\n}<\/code><\/pre>\n\n\n\n<p>This part was pretty standard and I had no problem.  The issue, however comes from the following functions: <code>setZipCode<\/code>, <code>setCode<\/code> , <code>addRow<\/code>, <code>removeRow<\/code>.  <\/p>\n\n\n\n<p>The standard way I thought it could work was to do something like the following in <code>setZipcode<\/code><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const setZipCode = (index, value) => {\n    let c = zipCodes;\n    c&#91;index] = value;\n    setZipCodes(c);\n  }<\/code><\/pre>\n\n\n\n<p>This, however, doesn&#8217;t work at all.  It seems like it should:  You get the list of current zipCodes (which is a state), assign it to another variable.  Then modify that variable.  Next, you setZipCodes to that new variable c.  <\/p>\n\n\n\n<p>Turns out this just doesn&#8217;t have the expected behavior.  It won&#8217;t update because it looks as if nothing has changed.  Instead, we need to use the spread operator.  This makes it seem like its getting a brand new array and then updates.  <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const setZipCode = (index, value) => {\n    let c = zipCodes;\n    c&#91;index] = value;\n    setZipCodes(c => &#91;...c]);\n  }<\/code><\/pre>\n\n\n\n<p>We can then use these spread operators on the other three functions: <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const setCost = (index, value) => {\n    const c = costs;\n    c&#91;index] = value;\n    setCosts(c => &#91;...c]);\n}<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>const addRow = (e) => {\n    \/\/ add zip code row for service.\n    var z = zipCodes;\n    setZipCodes(oldArray => &#91;...oldArray, z&#91;z.length - 1]]);\n\n    var c = costs;\n    setCosts(oldCosts => &#91;...oldCosts, c&#91;c.length - 1]]);\n}<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>const removeRow = (e) => {\n    setZipCodes(oldArray => &#91;...oldArray.slice(0, oldArray.length - 1)]);\n    setCosts(oldArray => &#91;...oldArray.slice(0, oldArray.length - 1)]);\n}<\/code><\/pre>\n\n\n\n<p>Now our form gets the expected behavior and we&#8217;re able to add new zip codes as well as edit them.  <\/p>\n\n\n\n<p>An alternative would be to just have one array full of objects <code>[{zip: 98611, cost: $30}, {zip: 97035, cost: $30}]<\/code> and have one <code>useState<\/code> for this.  <\/p>\n\n\n\n<p>Hopefully that helps someone save time as I burned a few hours trying to figure this out!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I spent a good portion of the work day today trying to code up a simple form to accept a list of zip codes and put them in an array. The final form looks as follows: As you can see there are a few zip codes and then we can click the &#8216;Add Zip Code&#8217;&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[943],"tags":[50,944,843,945],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/benincosa.com\/index.php?rest_route=\/wp\/v2\/posts\/3759"}],"collection":[{"href":"https:\/\/benincosa.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/benincosa.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/benincosa.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/benincosa.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=3759"}],"version-history":[{"count":1,"href":"https:\/\/benincosa.com\/index.php?rest_route=\/wp\/v2\/posts\/3759\/revisions"}],"predecessor-version":[{"id":3761,"href":"https:\/\/benincosa.com\/index.php?rest_route=\/wp\/v2\/posts\/3759\/revisions\/3761"}],"wp:attachment":[{"href":"https:\/\/benincosa.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3759"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/benincosa.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3759"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/benincosa.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3759"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}