0

I'm new at Reactjs and in this case, I'm trying to show a list of operations. I need to show only the LAST 10 operations of the list and I'm trying to do this using .splice() on the array. I tried a lot but couldn´t make it work. I'm getting the following error:

TypeError: list is not iterable.

Any idea how to do this?

This is my component code so far:

export default function ListOperations() {
  const dispatch = useDispatch();
  // const list = useSelector((state) => state.operations);
  const [list, setList] = React.useState({});

  React.useEffect(async () => {
    try {
      const response = await axios.get("http://localhost:3000/operation");

      dispatch({
        type: "LIST_OPERATIONS",
        list: response.data,
      });
    } catch (e) {
      swal("Error", e.message, "error");
    }
  }, []);

  const currentListCopy = [...list];

  if (currentListCopy >= 10) {
    currentListCopy.splice(10);
    setList(currentListCopy);
  }

  return (
    <div>
      <div>
        <h2>OPERATIONS HISTORY:</h2>
      </div>
      <table>
        <thead>
          <tr>
            <th>ID</th>
            <th>Reason</th>
            <th>Amount</th>
            <th>Date</th>
            <th>Type</th>
          </tr>
        </thead>
        <tbody>
          {list.map((oneOperation) =>
            oneOperation ? (
              <tr key={oneOperation.id}>
                <td>{oneOperation.id}</td>
                <td>{oneOperation.reason}</td>
                <td>{oneOperation.amount}</td>
                <td>{oneOperation.date}</td>
                <td>{oneOperation.type}</td>
              </tr>
            ) : null
          )}
        </tbody>
      </table>
    </div>
  );
}

UPDATED VERSION:

export default function ListOperations(){
    const dispatch = useDispatch();
    const storeList = useSelector((state) => state.operations);
    const [list, setList] = React.useState([]);

    React.useEffect(async () => {
        try{
            const response = await axios.get('http://localhost:3000/operation');

            dispatch({
                type: 'LIST_OPERATIONS',
                list: response.data
            })

            if(Array.isArray(storeList) && storeList.length){
                const currentListCopy = [...storeList];
                if(currentListCopy.length >= 10){
                    currentListCopy.splice(10);
                    setList(currentListCopy);
                }
            }
        }
        catch(e){
            swal("Error", e.message, "error");
        }
    }, [storeList]);
  • The infinite loop is caused by providing the `storeList` as dependency to the same `useEffect` hook, which is updating the same state. You should use two `useEffect` hooks. One to update the state on component rendering with empty dependency and the other `useEffect` one with `storeList` as dependency. I'm adding the whole code at the end of the answer. – Junaid Faryad Sep 26 '21 at 16:42
  • Glad, i was able to help. Also, if you find some answer useful to your question, you can accept it :) . – Junaid Faryad Sep 26 '21 at 16:57

2 Answers2

1

There are a couple of issues, which are causing the error and also, if the error is fixed, the fetched results will not be shown in the application.

Issue 1

const [list, setList] = React.useState({});

In the above code, you're initializing state as an object, which is causing the error list is not iterable, in the below code, when you're trying to use the spread operator to create an array of state object.

const currentListCopy = [...list];

Fix

You can fix this issue by initialing the list state as an empty array.

const [list, setList] = React.useState({});

Issue 2

The second issue is you're dispatching an action in the useEffect hook, but not getting the updated state from the store, since this line // const list = useSelector((state) => state.operations); is commented out. Since you're not fetching any state from store also nor updating the local state list, you'll not see any changes in the map function, as its empty, even though some data is being returned from the network in the API call.

Fix

If you wish to use the state from the store to update the local store, than you've to uncomment this line // const list = useSelector((state) => state.operations) and rename list to something else.

Also you need to move your splice code to the useEffect hook, so, whenever the list updated in the global state, your local state also updated accordingly.

React.useEffect(() => {
    if (Array.isArray(list) && list.length) { // assuming list is the global state and we need to ensure the list is valid array with some indexes in it.
      const currentListCopy = [...list];
      if(currentListCopy.length >= 10) { // as above answer point out
        currentListCopy.splice(10);
        setList(currentListCopy)
      }
    }
 }, [list]); // added list as a dependency to run the hook on any change in the list

Also, as above answer point out, you should avoid async functions in the useEffect.

Update

the complete code

export default function ListOperations() {
  const dispatch = useDispatch();
  const storeList = useSelector((state) => state.operations);
  const [list, setList] = React.useState([]);

  React.useEffect(async () => {
    try {
      const response = await axios.get("http://localhost:3000/operation");

      dispatch({
        type: "LIST_OPERATIONS",
        list: response.data,
      });
    } catch (e) {
      swal("Error", e.message, "error");
    }
  }, []);

  React.useEffect(() => {
    if (Array.isArray(storeList) && storeList.length) {
      const currentListCopy = [...storeList];
      if(currentListCopy.length >= 10) {
        currentListCopy.splice(10);
        setList(currentListCopy)
      }
    }
 }, [storeList]);

  return (
    <div>
      <div>
        <h2>OPERATIONS HISTORY:</h2>
      </div>
      <table>
        <thead>
          <tr>
            <th>ID</th>
            <th>Reason</th>
            <th>Amount</th>
            <th>Date</th>
            <th>Type</th>
          </tr>
        </thead>
        <tbody>
          {list.map((oneOperation) =>
            oneOperation ? (
              <tr key={oneOperation.id}>
                <td>{oneOperation.id}</td>
                <td>{oneOperation.reason}</td>
                <td>{oneOperation.amount}</td>
                <td>{oneOperation.date}</td>
                <td>{oneOperation.type}</td>
              </tr>
            ) : null
          )}
        </tbody>
      </table>
    </div>
  );
}
Junaid Faryad
  • 1,547
  • 1
  • 8
  • 15
0
if(currentListCopy >= 10){
    currentListCopy.splice(10);
    setList(currentListCopy)
}

you're missing "length" :

if(currentListCopy.length >= 10){
    currentListCopy.splice(10);
    setList(currentListCopy)
} 

also, you shouldn't use promise inside useEffect https://dev.to/danialdezfouli/what-s-wrong-with-the-async-function-in-useeffect-4jne

Kevin Gilbert
  • 903
  • 8
  • 21