FAQ: The State Hook - Arrays in State

This community-built FAQ covers the “Arrays in State” exercise from the lesson “The State Hook”.

Paths and Courses
This exercise can be found in the following Codecademy content:

Learn React

FAQs on the exercise Arrays in State

There are currently no frequently asked questions associated with this exercise – that’s where you come in! You can contribute to this section by offering your own questions, answers, or clarifications on this exercise. Ask or answer a question by clicking reply (reply) below.

If you’ve had an “aha” moment about the concepts, formatting, syntax, or anything else with this exercise, consider sharing those insights! Teaching others and answering their questions is one of the best ways to learn and stay sharp.

Join the Discussion. Help a fellow learner on their journey.

Ask or answer a question about this exercise by clicking reply (reply) below!
You can also find further discussion and get answers to your questions over in #get-help.

Agree with a comment or answer? Like (like) to up-vote the contribution!

Need broader help or resources? Head to #get-help and #community:tips-and-resources. If you are wanting feedback or inspiration for a project, check out #project.

Looking for motivation to keep learning? Join our wider discussions in #community

Learn more about how to use this guide.

Found a bug? Report it online, or post in #community:Codecademy-Bug-Reporting

Have a question about your account or billing? Reach out to our customer support team!

None of the above? Find out where to ask other questions here!

I can not seem to get removeItem working. I have even used CA’s solution and it doesn’t work. I also haven’t been able to find much documentation for using the state setter with callback functions, which is frustrating trying to figure the actual solution out for myself.

const removeItem = (targetIndex) => {
    setCart((prev) => {
      return prev.filter((item, index) => index !== targetIndex);
    });
  };

full code here:
https://gist.github.com/12d5c055bc1d807d0e9f9a6400e93fe0

2 Likes

Try this instead:
const removeItem = (targetIndex) => {
setCart((prev) => {
return prev.filter((item, index) => index !== targetIndex);
})
Or remove your semicolon

1 Like

Can someone please explain to me how this item in the ItemList gets passed around? I mean how does it know what item is clicked.

export default function ItemList({ items, onItemClick }) {
  const handleClick = ({ target }) => {
    const item = target.value;
    onItemClick(item);
  };
  return (
    <div>
      {items.map((item, index) => (
        <button className={buttonStyle} value={item} onClick={handleClick} key={index}>
          {item}
        </button>
      ))}
    </div>
  );
}

I guess this onItemClick(item) is confusing for me…does the item get passed back to addItem?:

  const addItem = (item) => {
    setCart((prev) => [...prev, item]);
  };

items is meant to contain a list of grocery items while onItemClick is meant to hold a reference to an event handling function. In GroceryCart.js, we import and then render ItemList components:
<ItemList items={produce} onItemClick={addItem} />
<ItemList items={pantryItems} onItemClick={addItem} />

In ItemList.js, the itemList function returns a div which consists of a series of buttons. We iterate over items using a variable called item and create buttons with attributes ... value={item} onClick={handleClick} ...

When a button is clicked, the handleClick event handling function is called. The handleClick function takes an event object as its parameter ({ target }). When a button is clicked, this event object contains lots of information about the button that was clicked. Using the statement const item = target.value;, we extract the information carried in the value attribute of the button (Recall we set value={item} when rendering our buttons via our map statement). So, suppose the “Carrots” button was clicked. This button will have a value of “Carrots” because of the attribute value={item}. By using const item = target.value;, we extract the value “Carrots” from the targeted button and assign this value to the const item in our handleClick function. In our last statement of the handleClick function namely onItemClick(item);, we make a call to a function. Recall the statement we used in the other file
<ItemList items={produce} onItemClick={addItem} />
onItemClick contains a reference to the addItem function. So, basically onItemClick(item); translates to the call addItem(item);

7 Likes

can someone explain to me what prev is?


const addItem = (item) => {
     setCart((prev) => {
        return [item, ...prev]
     });
}
1 Like

In this exercise, we are dealing with function components. If you look in the file GroceryCart.js, we have declared and initialized state with the statement:
const [cart, setCart] = useState([]);
The [] within the parentheses tells us that cart is going to be an array, initially an empty array. (If we had initialized it using somether other than [], such as a string or an object {}, then that would clue us in as to how we mean to store our data in our cart). We can use setCart to change the value of cart.

Coming back to the snippet you posted, the addItem function will be taking in an item which is going to be a string. If we did something like setCart([item]);, we will set the value of cart to be an array consisting of just this item. The problem is that doing so would overwrite our previous array. Initially we have an empty array, so overwriting that array with a new array i.e. [item] is fine. But, if our cart wasn’t empty but was something like ["Carrots", "Kale"], then something like setCart(["Lemons"]); would mean the old array would be lost and replaced by just ["Lemons"]. Obviously, we don’t want this and we want the item to be added to our existing cart. We want our cart to be ["Lemons", "Carrots", "Kale"].

If we don’t care about overwriting the old value, then setCart([item]); works fine.
But, if we want to preserve the existing old value or want to use the old value in some calculation, then the state setter function allows us this flexibility. All we need to do is just pick a parameter name and the state setter will automatically pass the previous value as the argument.
In our case, we have picked prev as the parameter name. There is nothing special about the name prev. We could have picked a different name, but prev is a good choice as it reminds us of the word previous. So our basic structure is setCart((prev) => { \\do stuff here with prev });
When we call our state setter with some item, the existing value of cart e.g. ["Carrots", "Kale"] will automatically be assigned to our parameter prev. Within the function body of our arrow function, we can use prev as we want. In this particular snippet, we create a new array whose first element is our new item and the ...prev spread syntax means that we expand the elements from our previous array. So, if item is "Lemons" and prev is ["Carrots", "Kale"] , then [item, ..prev] will be ["Lemons", "Carrots", "Kale"]. We then return this array so that the value of cart is updated to this new array.

8 Likes

Thanks so much! That was really helpful

I can not understand how this part of code is working? Can any-one explain it to me same as the above explanations for other parts of the code?
I can not understand what is difference between index & targetIndex, Also I dont understand the logic behind !== which as said in the body of the practice, It would result to True/False value.

const removeItem = (targetIndex) => {
setCart((prev) => {
return prev.filter((item, index) => index !== targetIndex);
});
};

// This function probably gets called on click on an element 
// which passes the index of the clicked item to the function
const removeItem = (targetIndex) => {

// setCart has probably been initialized using destructuring assignment syntax on useState
// When you call setCart you have access to the previously saved items in cart
setCart((prev) => {

// you filter the items previously stored in cart
// for every iteration of a prev item, you pass the index of it to the callback
// this line returns all items that don't have the same index as the targetIndex which was passed to removeItem
// index (the index of a previous item) !== (not equal to) targetIndex (index of item passed to removeItem function)
return prev.filter((item, index) => index !== targetIndex);
});
};
1 Like

You are referring to the snippet

const removeItem = (targetIndex) => {
    setCart((prev) => {
      return prev.filter((item, index) => index !== targetIndex);
    });
  };

We can better understand this snippet by reading through the related code in GroceryCart.js. We can see that cart is supposed to be an array (initialized as the empty array []) and that we can use the state setter setCart to make changes to cart.
Furthermore, as we add things to the cart, the items in the cart are displayed on screen because of the code

<ul>
        {cart.map((item, index) => (
          <li onClick={() => removeItem(index)} key={index}>
            {item}
          </li>
        ))}
</ul>

In this snippet, we are basically iterating over the items in the cart using the map method. If we just use one parameter in our arrow callback function e.g. cart.map((item) => ( ..., then the current value of the iteration will be assigned to the parameter (item in this example). If we use two parameters, then the first parameter will take on the current value while the second parameter will take on the index of the array at which the current value is located e.g. cart.map((item, index) => ( ...
Suppose our cart is ["Carrots", "Onions", "Carrots", "Kale"], then the map will create the following li elements
<li onClick={() => removeItem(0)} key=0>Carrots</li>
<li onClick={() => removeItem(1)} key=1>Onions</li>
<li onClick={() => removeItem(2)} key=2>Carrots</li>
<li onClick={() => removeItem(3)} key=3>Kale</li>

We can have duplicate items in our cart (such as two carrots in the example above), so just using the name of the item isn’t going to allow us to uniquely target a specific li element. However, every item in the cart is located in a unique position within the cart array. So if we use the index of the item as the key for our li elements, then every single li element will have a unique key and when we click on an item to remove it from the cart, we can use the key to target the correct item.

Now, let us come back to the snippet you posted. When we click on an item to remove it from the cart, then the index/key of the li element is passed as the argument to the event handling function removeItem. This unique number/key gets assigned to the parameter targetIndex. Within our function, we make a call to the state setter setCart. Since we have specified a parameter called prev, so the existing cart array is automatically assigned to the parameter prev. Then, within the body of our arrow function, we apply the filter method to our existing cart. We specify two parameters item and index. The filter method will iterate over the cart and in each iteration the current item will be assigned to item and the index of the the current item will be assigned to index. A test condition will be evaluated. Only those items that pass the condition will be retained while those that fail the test will be filtered out.

Suppose our cart is ["Carrots", "Onions", "Carrots", "Kale"] and we have clicked on the second Carrots element <li onClick={() => removeItem(2)} key=2>Carrots</li> to remove it from the cart. Then, the index/key of 2 will be passed as an argument to the removeItem function and will be assigned to its parameter targetIndex.
Then, the filter method is going to iterate over the existing cart and do the following comparisons:
First iteration: item will be "Carrots" and index will be 0. Is the index 0 NOT EQUAL to the target index of 2? Yes. True. So, we keep this item in our cart.
Second iteration: item will be "Onions" and index will be 1. Is the index 1 NOT EQUAL to the target index of 2? Yes. True. So, we keep this item in our cart.
Third iteration: item will be "Carrots" and index will be 2. Is the index 2 NOT EQUAL to the target index of 2? No. False. So, we filter out this item from our cart.
Fourth iteration: item will be "Kale" and index will be 3. Is the index 3 NOT EQUAL to the target index of 2? Yes. True. So, we keep this item in our cart.
A new array containing the elements ["Carrots", "Onions", "Kale"] will be created, but the old cart is still intact. So, we use the return keyword to return this array. The setCart state setter will then replace the previous cart with our new updated cart.

3 Likes

This work is through passing props, from GroceryCart to ItemList the functional component arguments are deconstructed to items, and onItemClick.
produce → items and addItem → onItemClick

<ItemList items={produce} onItemClick={addItem} />
export default function ItemList({ items, onItemClick }) {

Then in this component subdivide the task further, by doing a Items.map in the return, the interesting part comes in maps, it passes handleClick and value. This kinda allows you to pinpoint the specific value. Because basically we now have a bunch of event handler listening each individual item,due to each button text only holds a singular item in their value() due to the .map.

{items.map((item, index) => (
    <button value={item} onClick={handleClick} key={index}>
        {item}
    </button>
))}

Further then have to look at our handleClick, which destructures the normal event(e) argument into target, which then is converted into the value, which now hold the plain text(an arbitrary single produce/ item/ “Carrots”) of the item and we then use a the addItem function though onItemClick as this was pass down though our props.

const handleClick = ({ target }) => {
  const item = target.value;
  onItemClick(item);
};

Hello , I have a small dought in the delete function of this exercise and that is , if we are filtering the items based on index in the filter method then why we have passed the item variable as an argument in this method

cart is an array. In the removeItem function, we use setCart to update out cart. By using the parameter we choose to call prev, we have access to the previous state of our cart. We then update our cart by using the filter method on this cart i.e. prev.filter(...
You are correct that we are filtering the items based on the index, so we don’t seem to need the item parameter as we aren’t using this parameter to make our decisions. But, if we want to iterate over an array by using the filter method, then the first parameter of the filter method will always be assigned the “The current element being processed in the array.” (see documentation). If we include a second parameter, then that parameter will be assigned “The index of the current element being processed in the array.”. So, our call to the method looks like prev.filter((item, index)...
Suppose we just used one parameter like prev.filter((index)..., then the index won’t be assigned to this parameter, instead the current element being processed will be assigned to this parameter. The name index doesn’t mean anything. We could name our parameters something else e.g. instead of prev.filter((item, index)... we could name our parameters prev.filter((a, b)... but that is not a good choice for the parameter names because it doesn’t make for good readability.

All right ,thank you …

Prev is the previous value of the cart array.
Normally you can just do like this setCart(‘anothervalue’);
But when you use a function, the previous state value is always passed as a parameter.
You can actually call it whatever you want. In this course they choose to use prev.

1 Like