在使用数据库数据更新上下文后,React 'useContext' 钩子不会重新渲染

Posted

技术标签:

【中文标题】在使用数据库数据更新上下文后,React \'useContext\' 钩子不会重新渲染【英文标题】:React 'useContext' hook not re-rendering after context updates with database data在使用数据库数据更新上下文后,React 'useContext' 钩子不会重新渲染 【发布时间】:2021-09-21 08:43:40 【问题描述】:

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

最初定义了上下文,但很快就会从 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="#"
        
      />
      <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 

【问题讨论】:

setIdeas(getIdeas()) - getIdeas() 异步获取数据,因此像您这样调用它只会返回一个空数组。解决问题的一种方法是将setIdeas函数传递给getIdeas函数,当接收到数据时,调用setIdeas函数,从数据库中传入数据。 【参考方案1】:

首先,如果您只想获取一次数据,则需要使用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>
  );
;

【讨论】:

嗨,Tarik,感谢您提供代码,我很高兴将我的代码更改为异步代码。但是,我仍然有问题。信息被更新到上下文中,Displayer 组件中的 console.log 语句确认上下文被正确接收。如果功能组件明显在变化,那么为什么它不在上下文更新时重新渲染和更新? 没关系,它似乎已经解决了重新渲染问题。我不明白它为什么起作用,您能否解释一下这两个问题是如何相关的? 当然。你能展示一下你当前的代码是什么样的吗?你改变了什么? 我刚刚用我的新上下文文件更新了帖子。

以上是关于在使用数据库数据更新上下文后,React 'useContext' 钩子不会重新渲染的主要内容,如果未能解决你的问题,请参考以下文章

React/React Context API:使用 useEffect() 钩子时等待上下文值

在 axios 请求后自行更新状态?

状态更改后,上下文未更新 React Native 中的文本

在 React 上下文 API 中获取数据后状态不会改变

React UseEffect 依赖于上下文不会立即更新组件

React (Native) Context API 导致 Stack Navigator (React Navigation 5) 在状态更新后重新渲染