【问题标题】:How to make data persist on refresh React JS?如何使数据在刷新 React JS 时保持不变?
【发布时间】:2021-11-25 11:22:09
【问题描述】:

我有一个代码,我在其中安装了一个包含一些 firebase 数据的表,但由于某种原因,这些值消失了,我在接下来的 2 周里一直在努力解决这个问题,我还没有找到解决方案,我已经问过了已经两次了,到目前为止我已经尝试了所有方法,但它一直在消失。

重要更新 我只是想澄清以下显然我错了问题不是因为它是另一个问题中提到的嵌套集合。问题是因为我的“用户”在我刷新的过程中迷路了。

我将用户从登录带到应用程序,如下所示:

<Estudiantes user={user} />

然后我将它作为道具接收 function ListadoPedidos({user})

但是我迷路了,因为当我尝试使用我的火力基地时迷路了:

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

由于用户“迷路”,因此 uid 将为空。由于为 null,它永远不会到达集合和文档。

【问题讨论】:

  • 不确定,但它可能与 useEffect 中的 setItem 没有依赖数组有关。它在每次渲染时都运行,因此可能会将值设置为 []。
  • 真的吗?我在其他地方有类似的代码,它不会在刷新时消失。让我尝试添加它。
  • 这实际上解决了问题,但是,这带来了一个新问题,因为使用效果正在无限循环...... @nullptr 现在我有另一个问题,为什么它需要 while when 是1 个不需要的搜索集合?
  • 这正是现在发生的情况: 1. 我刷新 2. 数据消失 3. 数据又回来了,并且无限循环。
  • 无限循环是什么意思?发生了什么事?

标签: reactjs firebase google-cloud-firestore local-storage refresh


【解决方案1】:
  • 您的代码中的数据流有些矛盾,所以我修改了您的代码,它运行良好。
  • 您也可以尝试删除或添加按钮,它会修改 Firebase 集合,然后更新本地数据。
  • 您可以点击codesandbox previewer(不是浏览器)中的刷新按钮来观察数据更新的状态。

这里是代码片段:

  // Set value of `localStorage` to component state if it exist.
  useEffect(() => {
    const localStorageEstData = window.localStorage.getItem("estudiantes");
    localStorageEstData && setEstudiantes(JSON.parse(localStorageEstData));
  }, []);

  // Sync remote data from firebase to local component data state.
  useEffect(() => {
    // Subscribe onSnapshot
    const unSubscribe = onSnapshot(
      collection(db, "usuarios", user.id, "estudiantes"),
      (snapshot) => {
        const remoteDataSource = snapshot.docs.map((doc) => ({
          id: doc.id,
          ...doc.data()
        }));
        console.info(remoteDataSource);
        setEstudiantes(remoteDataSource);
      }
    );
    return () => {
      //unSubscribe when component unmount.
      unSubscribe();
    };
  }, [user.id]);

  // when `estudiantes` state update, `localStorage` will update too.
  useEffect(() => {
    window.localStorage.setItem("estudiantes", JSON.stringify(estudiantes));
  }, [estudiantes]);

这里是完整的代码示例:

希望能帮到你:)

【讨论】:

  • 谢谢!我要试试这个我只是想设置firebase部分(这将是你的模拟数据)
  • 它不适用于 firebase :c
  • @ReactPotato 我用firebase更新了答案,请检查并希望对你有所帮助。 (可以尝试添加、删除、刷新。)
  • 1. Attempted import error: 'collection' is not exported from 'firebase/firestore'. 因为我们的firebase包版本不一样,我用的是v9.1.2,不过没关系,你改成你的版本使用就好了。 2.好的....下班后我会做一个Collection inside a Collection 的样品。 @ReactPotato
  • 您好,我修改为多个集合。实际上没有太大区别,但我仍然希望它有所帮助。 @ReactPotato
【解决方案2】:

我有一个简单的解决方案给你。只需将 localStorage 的解析提升一级,将 preloadedState 作为 prop 传递到您的组件中,然后使用它来初始化您的 state 变量。

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

然后用 prop 初始化 state

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

最后,更新 useEffect 挂钩以在状态发生变化时保持状态。

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

完整代码

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

【讨论】:

  • 问题我如何使用我的用户变量?我无法从 firebase 获取 user.uid
  • @ReactPotato 我已经通过解构用户和 preloadedState 道具更新了我的代码示例。请注意,ListadoEstudiantes 道具被传播到 Listado 上。因此,您可以按预期简单地将用户道具传递给 ListadoEstudiantes。
  • 这就像@nullptr 的答案一样,唯一的问题是现在无休止地渲染。编辑:如果我从firebase设置下的useeffect中删除[estudiantes],它工作正常!只需更新您的答案:3
  • 似乎只有 1 个问题,但当我尝试删除某些内容时,它会在我刷新时回来
  • @ReactPotato 好的,我已经更新了代码!我只解决了您的问题,并没有解决您代码中的其他问题。但如果你愿意,我可以仔细看看重构你的代码?
【解决方案3】:

我怀疑这是因为这个 useEffect 没有依赖数组并且在每次渲染时都会运行。

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

尝试如下添加依赖数组:

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

当它在第一次渲染上运行时,这仍会将localStorage 设置为[]。但是当获取数据并设置estudiantes 时,localStorage 值将被更新。所以我添加了一个检查来检查它是否不是空数组。

把这个useEffect的依赖数组改成[]

  estudiantesRef.onSnapshot(snapshot => {
    const tempData = [];
    snapshot.forEach((doc) => {
      const data = doc.data();
      tempData.push(data);
    });
    setEstudiantes(tempData);
    console.log(estudiantes)
  })
 }, []);

【讨论】:

  • 现在刷新后数据消失了,但是循环消失了(因为我去掉了快照的数组依赖)。顺便说一句,在我添加 localStorage 之前它在做同样的行为,因为人们说这会修复,但它没有
  • 在更新本地存储之前检查值是否为 [] 会解决您的问题吗?
  • 我已经更新了答案@ReactPotato
  • 这也没用对不起,刷新后数据仍然消失。
  • 我们可以在聊天中讨论吗?聊天链接将在问题的 cmets 中
猜你喜欢
  • 2022-11-12
  • 1970-01-01
  • 2016-12-11
  • 2021-06-19
  • 1970-01-01
  • 1970-01-01
  • 2013-06-06
  • 2018-12-22
  • 1970-01-01
相关资源
最近更新 更多