Redux状态更改时,React不会重新呈现

时间:2019-09-19 17:08:22

标签: javascript reactjs redux

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

1 个答案:

答案 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;
}

模式1-以串行顺序(每次一个)运行每个异步提取:

const mappedTeam = [];
for (const val of team) {
  const mappedVal = await getValWithPhoto(val);
  mappedTeam.push(mappedVal);
}

return dispatch(...);

模式2-并行运行所有提取作业(同时)(我在上述回答中所做的事情):

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(...);
相关问题