Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make data persist on refresh React JS?

I have a code where I mount a table with some firebase data but for some reason the values disappear and I been struggling for the next 2 weeks trying to solve this issue I haven't found a solution to this and I have asked twice already and I have try everything so far but it keeps disappearing.

Important Update I just want to clarify the following apparently I was wrong the issue wasn't because it was a nested collection as someone mentioned in another question. The issue is because my "user" is getting lost in the process when I refresh.

I bring the user from the login to the app like this:

<Estudiantes user={user} />

and then I receive it as a props function ListadoPedidos({user})

but is getting lost and because is getting lost when I try to use my firebase as:

estudiantesRef = db.collection("usuarios").doc(user.uid).collection("estudiantes")

since the user is "lost" then the uid will be null. Since is null it will never reach the collection and the docs.

like image 566
ReactPotato Avatar asked Nov 07 '25 01:11

ReactPotato


2 Answers

I have a simple solution for you. Simply raise the parsing of localStorage up one level, passing the preloadedState into your component as a prop, and then using that to initialize your state variable.

const ListadoEstudiantes = (props) => {
  const estData = JSON.parse(window.localStorage.getItem('estudiantes'));
  return <Listado preloadedState={estData} {...props} />;
};

Then initialize state with the prop

  const initialState = props.preloadedState || [];
  const [estudiantesData, setEstudiantesData] = useState(initialState);

And finally, update the useEffect hook to persist state any time it changes.

  useEffect(() => {
    window.localStorage.setItem('estudiantes', JSON.stringify(estudiantes));
  }, [estudiantes]);

Full Code

import React, { useState, useEffect } from 'react';
import { db } from './firebase';
import { useHistory } from 'react-router-dom';
import './ListadoEstudiantes.css';
import {
  DataGrid,
  GridToolbarContainer,
  GridToolbarFilterButton,
  GridToolbarDensitySelector,
} from '@mui/x-data-grid';
import { Button, Container } from '@material-ui/core';
import { IconButton } from '@mui/material';
import PersonAddIcon from '@mui/icons-material/PersonAddSharp';
import ShoppingCartSharpIcon from '@mui/icons-material/ShoppingCartSharp';
import DeleteOutlinedIcon from '@mui/icons-material/DeleteOutlined';
import { Box } from '@mui/system';

const ListadoEstudiantes = (props) => {
  const estData = JSON.parse(window.localStorage.getItem('estudiantes'));
  return <Listado preloadedState={estData} {...props} />;
};

const Listado = ({ user, preloadedState }) => {
  const history = useHistory('');
  const crearEstudiante = () => {
    history.push('/Crear_Estudiante');
  };

  const initialState = preloadedState || [];
  const [estudiantesData, setEstudiantesData] = useState(initialState);

  const parseData = {
    pathname: '/Crear_Pedidos',
    data: estudiantesData,
  };

  const realizarPedidos = () => {
    if (estudiantesData == 0) {
      window.alert('Seleccione al menos un estudiante');
    } else {
      history.push(parseData);
    }
  };

  function CustomToolbar() {
    return (
      <GridToolbarContainer>
        <GridToolbarFilterButton />
        <GridToolbarDensitySelector />
      </GridToolbarContainer>
    );
  }

  const [estudiantes, setEstudiantes] = useState([]);
  const [selectionModel, setSelectionModel] = useState([]);
  const columns = [
    { field: 'id', headerName: 'ID', width: 100 },

    { field: 'nombre', headerName: 'Nombre', width: 200 },

    { field: 'colegio', headerName: 'Colegio', width: 250 },

    { field: 'grado', headerName: 'Grado', width: 150 },
    {
      field: 'delete',
      width: 75,
      sortable: false,
      disableColumnMenu: true,
      renderHeader: () => {
        return (
          <IconButton
            onClick={() => {
              const selectedIDs = new Set(selectionModel);
              estudiantes
                .filter((x) => selectedIDs.has(x.id))
                .map((x) => {
                  db.collection('usuarios')
                    .doc(user.uid)
                    .collection('estudiantes')
                    .doc(x.uid)
                    .delete();
                });
            }}
          >
            <DeleteOutlinedIcon />
          </IconButton>
        );
      },
    },
  ];

  const deleteProduct = (estudiante) => {
    if (window.confirm('Quiere borrar este estudiante ?')) {
      db.collection('usuarios').doc(user.uid).collection('estudiantes').doc(estudiante).delete();
    }
  };

  useEffect(() => {}, [estudiantesData]);

  const estudiantesRef = db.collection('usuarios').doc(user.uid).collection('estudiantes');
  useEffect(() => {
    estudiantesRef.onSnapshot((snapshot) => {
      const tempData = [];
      snapshot.forEach((doc) => {
        const data = doc.data();
        tempData.push(data);
      });
      setEstudiantes(tempData);
      console.log(estudiantes);
    });
  }, []);

  useEffect(() => {
    window.localStorage.setItem('estudiantes', JSON.stringify(estudiantes));
  }, [estudiantes]);

  return (
    <Container fixed>
      <Box mb={5} pt={2} sx={{ textAlign: 'center' }}>
        <Button
          startIcon={<PersonAddIcon />}
          variant="contained"
          color="primary"
          size="medium"
          onClick={crearEstudiante}
        >
          Crear Estudiantes
        </Button>
        <Box pl={25} pt={2} mb={2} sx={{ height: '390px', width: '850px', textAlign: 'center' }}>
          <DataGrid
            rows={estudiantes}
            columns={columns}
            pageSize={5}
            rowsPerPageOptions={[5]}
            components={{
              Toolbar: CustomToolbar,
            }}
            checkboxSelection
            //Store Data from the row in another variable
            onSelectionModelChange={(id) => {
              setSelectionModel(id);
              const selectedIDs = new Set(id);
              const selectedRowData = estudiantes.filter((row) => selectedIDs.has(row.id));
              setEstudiantesData(selectedRowData);
            }}
            {...estudiantes}
          />
        </Box>
        <Button
          startIcon={<ShoppingCartSharpIcon />}
          variant="contained"
          color="primary"
          size="medium"
          onClick={realizarPedidos}
        >
          Crear pedido
        </Button>
      </Box>
    </Container>
  );
};

like image 112
Benjamin Avatar answered Nov 09 '25 14:11

Benjamin


I suspect that it's because this useEffect does not have a dependency array and is bring run on every render.

useEffect (() => {
   window.localStorage.setItem("estudiantes", JSON.stringify(estudiantes))
 })

Try adding a dependency array as follows:

useEffect (() => {
if (estudiantes && estudiantes.length>0)
   window.localStorage.setItem("estudiantes", JSON.stringify(estudiantes))
 },[estudiantes])

This will still set the localStorage to [] when it runs on the first render. But when the data is fetched and estudiantes is set, the localStorage value will be updated. So I've added a check to check if it's not the empty array.

Change the dependency array of this useEffect to []:

  estudiantesRef.onSnapshot(snapshot => {
    const tempData = [];
    snapshot.forEach((doc) => {
      const data = doc.data();
      tempData.push(data);
    });
    setEstudiantes(tempData);
    console.log(estudiantes)
  })
 }, []);
like image 32
nullptr Avatar answered Nov 09 '25 14:11

nullptr