首页 > 解决方案 > 在使用数据库数据更新上下文后,React 'useContext' 挂钩不会重新渲染

问题描述

我正在使用 React 的 Context API 来共享我的大多数组件需要的数据。

Context 最初是定义的,但很快就会从 Firebase 数据库接收数据(请参阅 参考资料IdeaContext.tsx)。我在一个功能组件和显示组件中定义了上下文,它根据接收到的信息返回一张小卡片。

但是,当我使用 Yarn 启动开发服务器时,组件不会呈现。相反,为了让它渲染,我必须console.log('something')在显示组件内部编写,然后它突然重新渲染。但是,当我刷新服务器时,它再次不会呈现。

如何使我的组件立即呈现(或至少在使用数据库中的数据更新上下文之后?)


代码:

上下文定义:

import React, { createContext, useEffect, useState } from "react";
import { IdeaContextType, Idea } from "../t";
import {ideasRef} from './firebase'

function getIdeas() {
  var arr: Array<Idea> = [];

  ideasRef.on('value', (snapshot) => {
      let items = snapshot.val()

      snapshot.forEach( (idea) => {
        const obj = idea.val()
        arr.push({
          title: obj.title,
          description: obj.description,
          keyID: obj.keyID
        })
        console.log(arr)
      })
  })
  return arr
}

const IdeaContextDefaultValues: IdeaContextType = {
  ideas: [],
  setIdeas: () => {},
};

const IdeaContext = createContext<IdeaContextType>(IdeaContextDefaultValues)

const IdeaContextProvider: React.FC = ({ children }) => {
  const [ideas, setIdeas] = useState<Array<Idea>>(
    IdeaContextDefaultValues.ideas);
  
  useEffect( ()=> {
      console.log('getting info')
      setIdeas(getIdeas())
  }, [])


  useEffect( () => {
    console.log('idea change: ', ideas)
  }, [ideas])

  return (
    <IdeaContext.Provider value={{ ideas, setIdeas }}>
      {children}
    </IdeaContext.Provider>
  );
};

显示器和卡组件

import React, { FC, ReactElement, useContext } from "react";
import IdeaCreator from "./IdeaCreator";
import { IdeaContext } from "./IdeaContext";
import { Idea } from "../t";
import { Link } from "react-router-dom";

const IdeaPost:React.FC<Idea> = ({title, keyID, description}):ReactElement => {

  console.log('Received',title,description,keyID)
  return (
    <div className="max-w-sm rounded overflow-hidden shadow-lg">
      <img
        className="w-full"
        src="#"
        alt="Oopsy daisy"
      />
      <div className="px-6 py-4">
        <div className="font-bold text-xl mb-2"> <Link to={"ideas/" + keyID} key= {keyID}> {title}</Link>  </div>
        <p className="text-gray-700 text-base">{description}</p>
      </div>
    </div>
  );
};

const IdeaDisplay:FC<any> = (props:any):ReactElement => {


  const { ideas, setIdeas } = useContext(IdeaContext)
  console.log('Ideas in display: ', ideas)
  console.log('test') //This is what I comment and uncommend to get it to show

  return (
    <div className="flex flex-wrap ">
      {ideas.map((idea) => {
        console.log(idea)
        console.log('Sending',idea.title,idea.description,idea.keyID)
        console.log(typeof idea.keyID)
        return ( 
          <IdeaPost
            title={idea.title}
            description={idea.description}
            keyID = {idea.keyID}
            key = {idea.keyID * 100}
          />
        );
      })}
    </div>
  );
};

export default IdeaDisplay;

解决方案代码:

import React, { createContext, useEffect, useState } from "react";
import { IdeaContextType, Idea } from "../t";
import {ideasRef} from './firebase'

async function getIdeas() {
  var arr: Array<Idea> = [];

  const snapshot = await ideasRef.once("value");

  snapshot.forEach((idea) => {
    const obj = idea.val();
    arr.push({
      title: obj.title,
      description: obj.description,
      keyID: obj.keyID,
    });
    console.log(arr);
  });

  return arr
}

const IdeaContextDefaultValues: IdeaContextType = {
  ideas: [],
  setIdeas: () => {},
};

const IdeaContext = createContext<IdeaContextType>(IdeaContextDefaultValues)

const IdeaContextProvider: React.FC = ({ children }) => {
  const [ideas, setIdeas] = useState<Array<Idea>>(
    IdeaContextDefaultValues.ideas);
  
    useEffect(() => {
      console.log("getting info");
      const setup = async () => {
        const ideas = await getIdeas();
        setIdeas(ideas);


      };

      setup()


    }, []);
  


  useEffect( () => {
    console.log('idea change: ', ideas)

    const updateDatabase = async () => {
        await ideasRef.update(ideas)
        console.log('updated database')
    }

    updateDatabase()

  }, [ideas])

  return (
    <IdeaContext.Provider value={{ ideas, setIdeas }}>
      {children}
    </IdeaContext.Provider>
  );
};

export {IdeaContext, IdeaContextProvider} 

标签: javascriptreactjsfirebasereact-context

解决方案


首先,如果您只想获取一次数据,则需要使用once而不是。on如果您想使用实时侦听器,您可以将 发送setIdeas到您的函数。还要小心async/away调用 Firebase sdk。您的代码可能如下所示:

import React, { createContext, useEffect, useState } from "react";
import { IdeaContextType, Idea } from "../t";
import { ideasRef } from "./firebase";

async function getIdeas() {
  var arr: Array<Idea> = [];

  const snapshot = await ideasRef.once("value");

  let items = snapshot.val();

  snapshot.forEach((idea) => {
    const obj = idea.val();
    arr.push({
      title: obj.title,
      description: obj.description,
      keyID: obj.keyID,
    });
    console.log(arr);
  });

  return arr;
}

const IdeaContextDefaultValues: IdeaContextType = {
  ideas: [],
  setIdeas: () => {},
};

const IdeaContext = createContext < IdeaContextType > IdeaContextDefaultValues;

const IdeaContextProvider: React.FC = ({ children }) => {
  const [ideas, setIdeas] =
    useState < Array < Idea >> IdeaContextDefaultValues.ideas;

  useEffect(() => {
    console.log("getting info");
    const getData = async () => {
      const ideas = await getIdeas();
      setIdeas(ideas);
    };
  }, []);

  useEffect(() => {
    console.log("idea change: ", ideas);
  }, [ideas]);

  return (
    <IdeaContext.Provider value={{ ideas, setIdeas }}>
      {children}
    </IdeaContext.Provider>
  );
};


推荐阅读