Search code examples
arraysreactjssplice

How to use .splice() property at React?


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]);

Solution

  • 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>
      );
    }