Dalam aplikasi reaksi saya memiliki pemetaan beberapa data yang diambil dari toko Redux ke beberapa komponen

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

Di sini team adalah prop yang dipetakan dari redux Store. Data di toko Redux diambil dari database saat pengguna membuka Aplikasi. Ini berfungsi, karena saya telah mencatat perubahan prop team dan itu benar-benar diperbarui seperti yang diharapkan.

Masalahnya adalah bahwa bahkan setelah prop diperbarui, yang mungkin terjadi beberapa detik setelah render awal, aplikasi tidak akan merender ulang untuk mencerminkan perubahan prop ini. Tetapi jika setelah itu komponen ini dilepas dan dipasang kembali, komponen ini akan dirender dengan benar. Juga antara melepas dan memasang kembali toko redux tidak diperbarui dan tidak ada yang terjadi dalam siklus hidup pemasangan.

Adakah yang tahu apa yang dapat menyebabkan perilaku ini? Terima kasih sebelumnya!

Memperbarui:

Inilah komponen lengkapnya (menggunakan TypeScript)

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);

Juga di sini adalah peredam yang bertanggung jawab atas pembaruan properti toko itu

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

Pembaruan #2 :

Berikut adalah tindakan yang mengirim 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
Adrian Pascu 19 September 2019, 20:08

1 menjawab

Jawaban Terbaik

Tindakan asinkron Anda bermasalah. Khususnya kode ini:

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

Akan secara asinkron bermutasi status penyimpanan di luar tindakan apa pun di masa mendatang. Ini tidak diperbolehkan. Coba versi ini sebagai gantinya.

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

Versi ini menunggu pengambilan asinkron sebelum mengirimkan tindakan TEAM_RECIEVED.

Sedikit penjelasan lagi:

Array.foreach(fungsi async) hanya akan mengantri banyak pekerjaan async tetapi foreach akan segera kembali. Anda harus menunggu semua pekerjaan async. Jadi Anda tidak bisa menggunakan array.foreach(). Solusinya adalah salah satu dari 2 pola ini:

Asumsikan Anda memiliki metode ini:

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

Pola 1 - jalankan setiap pengambilan async dalam urutan serial (satu per satu):

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

return dispatch(...);

Pola 2 - jalankan semua pekerjaan pengambilan secara paralel (pada saat yang sama) (apa yang saya lakukan dalam jawaban saya di atas):

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(...);
2
Brandon 19 September 2019, 18:11