在React应用程序中,我具有从Redux存储获取的某些数据到某些组件的映射
{this.props.team && this.props.team.map((value: User, index: number) =>
(<Card key={index} className="team-card">
<CardMedia style={{
backgroundImage: `url(${value.photoURL})`
}} />
<Typography use={"headline4"}>{value.displayName}</Typography>
<Typography use={"body1"}>{value.description}</Typography>
<CardActions>
<CardActionButtons>
{/* TODO: Add the ability to go to About to a specific team member card */}
<CardActionButton>Vezi profilul</CardActionButton>
</CardActionButtons>
</CardActions>
</Card>)
)}
此处team
是从redux商店映射的道具。用户打开应用程序时,将从数据库中获取Redux存储中的数据。这行得通,因为我已经记录了team
道具的更改,并且实际上按预期进行了更新。
问题在于,即使在道具更新后(可能在初始渲染后一秒钟发生),应用也不会重新渲染以反映道具更改。但是,如果在此之后卸载并重新安装了该组件,则它将正确呈现。同样,在卸载和重新安装之间,redux存储不会得到更新,并且在安装生命周期中什么也没有发生。
有人知道导致这种现象的原因吗?预先感谢!
更新:
这里是完整的组件(它使用打字稿)
import React from "react"
import { Article } from "../../models/Article";
import Carousel, { CarouselItem } from "../Carousel/Carousel";
import "./Homescreen.scss";
import { connect } from "react-redux";
import AppState from "../../store/AppState";
import { Typography, Card, CardMedia, CardActionButton, CardActions, CardActionButtons } from "rmwc"
import User from "../../models/User";
import ArticleCompact from "../Article/ArticleCompact/ArticleCompact";
import Navbar from "../Navbar/Navbar";
class Homescreen extends React.Component<HomescreenProps, {}>{
constructor(props: Readonly<HomescreenProps>) {
super(props);
}
render() {
return (
<main>
<Navbar></Navbar>
<div id="container">
<div id="content">
<Carousel className="homescreen-carousel" items={this.props.carouselItems} speed={5}></Carousel>
{this.props.recentArticles.length !== 0 && (<section id="homescreen-recent-articles">
<Typography use={"headline2"} className="homescreen-head">Recente</Typography>
<hr className="homescreen-hr" />
{this.props.recentArticles[0] && (
<ArticleCompact URL={"/article/" + this.props.recentArticles[0].url} image={this.props.recentArticles[0].coverURL}
text={this.props.recentArticles[0].shortVersion} title={this.props.recentArticles[0].title}
type={"left-to-right"}
/>)}
{this.props.recentArticles[1] && (<ArticleCompact URL={"/article/" + this.props.recentArticles[1].url} image={this.props.recentArticles[1].coverURL}
text={this.props.recentArticles[1].shortVersion} title={this.props.recentArticles[1].title}
type={"right-to-left"}
/>)}
</section>)}
<section id="homescreen-team">
<Typography use={"headline2"} className="homescreen-head">Echipa</Typography>
<hr className="homescreen-hr" />
<div id="team-cards">
{this.props.team && this.props.team.map((value: User, index: number) =>
(<Card key={index} className="team-card">
<CardMedia style={{
backgroundImage: `url(${value.photoURL})`
}} />
<Typography use={"headline4"}>{value.displayName}</Typography>
<Typography use={"body1"}>{value.description}</Typography>
<CardActions>
<CardActionButtons>
{/* TODO: Add the ability to go to About to a specific team member card */}
<CardActionButton>Vezi profilul</CardActionButton>
</CardActionButtons>
</CardActions>
</Card>)
)}
</div>
</section>
</div>
</div>
</main>
)
}
}
function mapStateToProps(state: Readonly<AppState>) {
const items: CarouselItem[] = [] as CarouselItem[];
const articles: Article[] = [];
if (state.articles.featured.length !== 0)
state.articles.featured.map((item: Article) => {
return {
image: item.coverURL,
title: item.title,
path: "/article/"+item.url
}
}
).forEach((value: CarouselItem) => {
items.push(value);
})
//Map the first 4 recent articles to CarouselItems and push them to an array
state.articles.recent.map(async (item: Article) => (
{
image: URL.createObjectURL(await fetch(item.coverURL).then(res => res.blob())),
title: item.title,
path: "/article/"+item.url
})
).forEach(async (value, index) => {
if (index < 4)
items.push(await value);
});
//Map the last 2 recent articles to props
for (let [index, article] of state.articles.recent.entries()) {
if (index >= 4)
articles.push(article)
}
return {
carouselItems: items,
recentArticles: articles,
team: state.metadata.team
}
}
export default connect(mapStateToProps)(Homescreen);
这也是负责该商店属性更新的简化程序
export default function userReducer(state: Readonly<MetadataState> | undefined = initialAppState.metadata, action: MetadataActions): MetadataState {
switch (action.type) {
case 'TEAM_RECIEVED': return { ...state, team: action.payload };
default: return state;
}
}
更新#2:
这是使TEAM_RECIEVED
沮丧的动作
export function retrieveTeam() {
return async (dispatch: Dispatch) => {
const team = await getTeam_firestore();
const mappedTeam: User[] = [];
team.forEach(async (val: User, index: number) => mappedTeam.push({
...val,
photoURL: val.photoURL !== null ? URL.createObjectURL(await fetch(val.photoURL!!).then(res => res.blob())) : null
}));
console.log('Got team')
return dispatch({
type: 'TEAM_RECIEVED',
payload: mappedTeam
})
}
}
答案 0 :(得分:2)
您的异步操作是越野车。特别是这段代码:
team.forEach(async (val: User, index: number) => mappedTeam.push({
...val,
photoURL: val.photoURL !== null ? URL.createObjectURL(await
fetch(val.photoURL!!).then(res => res.blob())) : null
}));
将来会在任何动作之外异步变异存储状态。这是不允许的。尝试使用此版本。
export function retrieveTeam() {
return async (dispatch: Dispatch) => {
const team = await getTeam_firestore();
const mappedTeam: User[] = await Promise.all(team.map(
async (val: User, index: number) => {
const result = {...val};
if (result.photoURL !== null) {
const response = await fetch(result.photoURL);
const blob = await response.blob();
result.photoURL = URL.createObjectURL(blob);
}
return result;
}));
console.log('Got team')
return dispatch({
type: 'TEAM_RECIEVED',
payload: mappedTeam
})
}
}
此版本在分派TEAM_RECIEVED操作之前等待异步获取。
更多说明:
array.foreach(异步函数)只会将一堆异步工作排队,但是foreach将立即返回。您需要等待所有异步工作。因此,您不能使用array.foreach()。解决方案是以下两种模式之一:
假设您具有此方法:
async function getValWithPhoto(val) {
const result = {...val};
if (result.photoURL !== null) {
const response = await fetch(result.photoURL);
const blob = await response.blob();
result.photoURL = URL.createObjectURL(blob);
}
return result;
}
const mappedTeam = [];
for (const val of team) {
const mappedVal = await getValWithPhoto(val);
mappedTeam.push(mappedVal);
}
return dispatch(...);
const arrayOfPromises = team.map(val => getValWithPhoto(val));
// Use Promise.all() to turn the array of promises into a single
// promise: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
const promise = Promise.all(arrayOfPromises);
// now await that promise, which will return array of results
const mappedTeam = await promise;
return dispatch(...);