在使用数据库数据更新上下文后,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() 钩子时等待上下文值
状态更改后,上下文未更新 React Native 中的文本
React UseEffect 依赖于上下文不会立即更新组件
React (Native) Context API 导致 Stack Navigator (React Navigation 5) 在状态更新后重新渲染