redux 渲染模态时,React使用效果导致问题

4szc88ey  于 2023-03-02  发布在  React
关注(0)|答案(1)|浏览(126)

我是一个新的react开发者,我的问题是关于一个模型的渲染问题,下面是我的代码:

import * as React from 'react'
import { Typography, IconButton, Box, Link, Grid, FormGroup, FormControlLabel, Switch, Accordion, AccordionSummary, AccordionDetails } from '@mui/material'
import TwinLayout from '../../components/TwinLayout'
import EditIcon from '@mui/icons-material/Edit'
import PinDropIcon from '@mui/icons-material/PinDrop'
import PersonIcon from '@mui/icons-material/Person'
import { createAsset, getAssetTypes, createMonitoringDevice, fetchSites, fetchSiteBySiteId } from '../../api'
import { useTranslation } from 'react-i18next'
import ListAvatarData from '../../components/ListAvatarData'
import ListAvatarDataTitle from '../../components/ListAvatarDataTitle'
import { useState } from 'react'
import { Buffer } from 'buffer'
import styles from './style.module.css'
import { Area, Asset, AssetType, MonitoringDevice, MonitoringDeviceType, Site } from '../../types/entities'
import { connect } from 'react-redux'
import EditSiteModal from '../../components/EditSiteModal'
import { setSelectedAsset, setSelectedSite } from '../../actions'
import CreateAssetModal from '../../components/CreateAssetModal'
import CreateMonitoringDeviceModal from '../../components/CreateMonitoringDeviceModal'
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
import { useNavigate } from 'react-router-dom'

function SiteDetail({ selectedSite, updateSelected, updateSelectedSite }: { selectedSite: Site; updateSelected: any; updateSelectedSite: any }) {
    const [assetAreas, setAssetAreas] = React.useState<Area[]>([])
    const [monitoringDeviceAreas, setMonitoringDeviceAreas] = React.useState<Area[]>([])
    const [assetTypes, setAssetTypes] = React.useState<AssetType[]>([])
    const [monitoringDeviceTypes, setMonitoringDeviceTypes] = React.useState<any[]>([])
    const site = selectedSite
    const { t } = useTranslation()
    const [open, setOpen] = React.useState<boolean>(false)
    const [data, setData] = React.useState<boolean>(false)
    const [assetOpen, setAssetOpen] = React.useState<boolean>(false)
    const [monitoringDeviceOpen, setMonitoringDeviceOpen] = React.useState<boolean>(false)
    const [deleteItem, setDeleteItem] = useState(false)
    const [assetInputs, setAssetInputs] = React.useState<Partial<Asset>>({ name: '', type: '', area: '', siteId: selectedSite.id, serialNumber: '' })
    const [monitoringDeviceInputs, setMonitoringDeviceInputs] = React.useState<Partial<MonitoringDevice>>({
        name: '',
        type: '',
        area: '',
        siteId: selectedSite.id,
        imageRenderLocation: '',
    })

    const [isExist, setIsExist] = React.useState<any>({
        isImageExist: true,
        isFloorPlanExist: true,
        isMonitoringDeviceNameExist: true,
        isEditMonitoringDeviceNameExist: true,
    })

    const monitoringDeviceTypeList = Object.entries(MonitoringDeviceType)
        .filter((e) => isNaN(Number(e[0])))
        .map((e) => ({ id: e[0], name: e[0] }))

    // const searchList = [
    //     { id: 0, name: 'Asset' },
    //     { id: 1, name: 'Monitoring Device' },
    // ]
    const navigate = useNavigate()

    const fetchData = async () => {
        const assetTypeList = await getAssetTypes()

        setAssetTypes(assetTypeList.data.assetTypes.result)
        setMonitoringDeviceTypes(monitoringDeviceTypeList)

        if (selectedSite?.areas != undefined) {
            setAssetAreas(selectedSite?.areas)
            setMonitoringDeviceAreas(selectedSite?.areas)
        }

        let selectedSiteId = selectedSite.id

        if (selectedSiteId !== undefined) {
            const sites = await fetchSiteBySiteId([selectedSiteId])

            console.log('sites')
            console.log(sites.data.sites[0])

            updateSelectedSite({ ...selectedSite, assets: sites.data.sites[0].assets.result })

            console.log('selectedSite')
            console.log(selectedSite)

            if (sites.data) setData(sites.data)
            else console.log('Something wrong')
        }
    }

    const setIsPropertyExist = (name: 'isImageExist' | 'isFloorPlanExist' | 'isAssetExist' | 'isMonitoringDeviceExist', val: boolean) => {
        setIsExist((prevState: any) => ({
            ...prevState,
            [name]: val,
        }))
    }

    const handleAssetOpen = async () => {
        setAssetOpen(true)
    }

    const handleMonitoringDeviceOpen = () => {
        setMonitoringDeviceOpen(true)
    }

    const handleClose = () => {
        setOpen(false)
    }

    const handleAssetClose = () => {
        setAssetOpen(false)
    }

    const handleMonitoringDeviceClose = () => {
        setMonitoringDeviceOpen(false)
        setIsPropertyExist(isExist.isMonitoringDeviceNameExist, true)
    }

    const handleDeleteItem = () => {
        setDeleteItem(false)
    }

    const handleAssetClick = (asset: Asset) => {
        updateSelected(asset)
        navigate(`/console/assets/${asset.id}`)
    }

    const handleChangeAssetType = (e: any) => {
        setAssetInputs((prevState) => ({
            ...prevState,
            type: e.target.value,
        }))
    }

    const handleChangeMonitoringDeviceType = (e: any) => {
        setMonitoringDeviceInputs((prevState) => ({
            ...prevState,
            type: e.target.value,
        }))
    }

    const handleChangeAssetArea = (e: any) => {
        setAssetInputs((prevState) => ({
            ...prevState,
            area: e.target.value,
        }))
    }

    const handleChangeMonitoringDeviceArea = (e: any) => {
        setMonitoringDeviceInputs((prevState) => ({
            ...prevState,
            area: e.target.value,
        }))
    }

    const handleAssetCreate = async () => {
        if (selectedSite.id && assetInputs.name && assetInputs.type && assetInputs.area && assetInputs.serialNumber) {
            await createAsset(selectedSite.id, assetInputs.name, assetInputs.type, assetInputs.area, assetInputs.serialNumber)
        }

        setAssetOpen(false)
        setAssetInputs({
            name: assetInputs.name,
            type: assetInputs.type,
            area: assetInputs.area,
            siteId: selectedSite.id,
            serialNumber: assetInputs.serialNumber,
        })
        await fetchData()
    }

    const handleMonitoringDeviceCreate = async () => {
        if (monitoringDeviceInputs.name === '') {
            setIsPropertyExist(isExist.isMonitoringDevice, false)
            return
        }

        if (selectedSite.id && monitoringDeviceInputs.name && monitoringDeviceInputs.type && monitoringDeviceInputs.area && monitoringDeviceInputs.imageRenderLocation) {
            await createMonitoringDevice(
                selectedSite.id,
                monitoringDeviceInputs.name,
                monitoringDeviceInputs.type,
                monitoringDeviceInputs.area,
                monitoringDeviceInputs.imageRenderLocation
            )
        }

        setMonitoringDeviceOpen(false)
        setMonitoringDeviceInputs({
            name: monitoringDeviceInputs.name,
            type: monitoringDeviceInputs.type,
            area: monitoringDeviceInputs.area,
            siteId: selectedSite.id,
            imageRenderLocation: monitoringDeviceInputs.imageRenderLocation,
        })
        await fetchData()
    }

    const handleAssetInputChange = (e: any, name: string) => {
        //handle asset inputs
        setAssetInputs((prevState) => ({
            ...prevState,
            [name]: e.target.value,
        }))
    }

    const handleMonitoringDeviceInputChange = (e: any, name: string) => {
        //handle monitoring device inputs
        setMonitoringDeviceInputs((prevState) => ({
            ...prevState,
            [name]: e.target.value,
        }))
    }

    React.useEffect(() => {
        if (deleteItem) fetchData()
    }, [deleteItem])

    React.useEffect(() => {
        if (selectedSite.image && isExist.isImageExist) fetchImages(selectedSite.image, 'image')
        if (selectedSite.floorPlan && isExist.isFloorPlanExist) fetchImages(selectedSite.floorPlan, 'floorPlan')
    }, [selectedSite])

    const fetchImages = async (url: string, type: string) => {
        const res = await fetch(url, { method: 'GET', cache: 'reload' })
        if (type === 'image') setIsPropertyExist(isExist.isImageExist, res.ok)
        if (type === 'floorPlan') setIsPropertyExist(isExist.isFloorPlanExist, res.ok)
    }

    return (
        <TwinLayout
            side={
                <div className="w-100 h-100">
                    <div className="flex row" style={{ alignItems: 'center' }}>
                        <Typography className={styles.siteDetails}>{t('Site Details')}</Typography>
                        <IconButton onClick={() => setOpen(true)} color="primary">
                            <EditIcon className={styles.EditIcon} />
                            <Typography className="mr-10" fontSize={14}>
                                {t('Edit')}
                            </Typography>
                        </IconButton>
                    </div>
                    <div className="detailFields">
                        <PinDropIcon fontSize="small"></PinDropIcon>
                        <Typography fontSize={14}>{t('Address')}</Typography>
                    </div>
                    <Typography className="mt-4" fontSize={12}>
                        {selectedSite?.address}
                    </Typography>
                    <div className="detailFields">
                        <PersonIcon fontSize="small"></PersonIcon>
                        <Typography fontSize={14}>{t('Contact People')}</Typography>
                    </div>
                    {selectedSite?.contactPersons?.map((person, i) => {
                        return (
                            <div key={i}>
                                <Typography fontSize={12} className="mt-4">
                                    {person.name}
                                </Typography>
                                <Typography fontSize={12} color={'#969696'}>
                                    {`${person.phoneNumber} ${person.email}`}
                                </Typography>
                            </div>
                        )
                    })}
                    <Typography fontWeight={600} className="mt-13 mb-13" fontSize={14}>
                        {t('Site visuals')}
                    </Typography>
                    {isExist.isImageExist && (
                        <Box borderRadius={1} className="mr-10" component="img" alt={t('Site Image')} src={selectedSite?.image + '#' + Date.now()} width={'13vh'} height={'6vw'} />
                    )}
                    {isExist.isFloorPlanExist && (
                        <Box borderRadius={1} component="img" alt={t('Site Floor Plan')} src={selectedSite?.floorPlan + '#' + Date.now()} width={'13vh'} height={'6vw'} />
                    )}
                    <Typography fontWeight={600} className="mt-13 mb-13" fontSize={14}>
                        {t('Projects')} (5)
                    </Typography>

                    {[0, 1, 2, 3, 4].map((i) => {
                        return (
                            <Box
                                key={i}
                                borderRadius={1}
                                className="mr-10"
                                component="img"
                                alt="Factory Picture"
                                src={'https://scdn.emotorsdirect.ca/img/knowledge-center/hero/choosing-the-right-industrial-motor-for-your-application.jpeg@1080w.webp'}
                                width={'19.5vh'}
                                height={'9vw'}
                            />
                        )
                    })}
                </div>
            }>
            <Grid container justifyContent="flex-end" paddingBottom={5}>
                <Link sx={{ marginTop: '7px' }} href="#">
                    <Typography onClick={handleMonitoringDeviceOpen}>{t('+ Add monitoring device')}</Typography>
                </Link>
                <Link sx={{ marginTop: '7px' }} onClick={handleAssetOpen} href="#" paddingLeft={5} paddingRight={5}>
                    <Typography>{t('+ Add asset')}</Typography>
                </Link>
                <FormGroup>
                    <FormControlLabel labelPlacement="start" control={<Switch defaultChecked />} label={t('Show drawing')} />
                </FormGroup>
            </Grid>
            <Grid container justifyContent="flex-end" paddingBottom={5}>
                <FormGroup>
                    <FormControlLabel labelPlacement="start" control={<Switch defaultChecked />} label={t('Group by area')} />
                </FormGroup>
            </Grid>
            <Accordion>
                <AccordionSummary expandIcon={<ExpandMoreIcon />} aria-controls="panel1a-content" id="panel1a-header">
                    <div className={styles.divAvatarDataTitle}>
                        <ListAvatarDataTitle count={site.monitoringDevices?.length} type={'Monitoring Devices'}></ListAvatarDataTitle>
                    </div>
                </AccordionSummary>
                <AccordionDetails>
                    <ListAvatarData
                        handleClick={undefined}
                        setDelete={handleDeleteItem}
                        data={site.monitoringDevices}
                        type={'Monitoring Devices'}
                        fetchData={undefined}></ListAvatarData>
                    <div className={styles.divAvatarDataTitle}></div>
                </AccordionDetails>
            </Accordion>
            <Accordion>
                <AccordionSummary expandIcon={<ExpandMoreIcon />} aria-controls="panel1a-content" id="panel1a-header">
                    <div className={styles.divAvatarDataTitle}>
                        <ListAvatarDataTitle count={site.assets?.result?.length} type={'Assets'}></ListAvatarDataTitle>
                    </div>
                </AccordionSummary>
                <AccordionDetails>
                    <ListAvatarData handleClick={handleAssetClick} setDelete={handleDeleteItem} data={site.assets?.result} type={'Assets'} fetchData={undefined}></ListAvatarData>
                </AccordionDetails>
            </Accordion>

            <EditSiteModal
                setIsPropertyExist={setIsPropertyExist}
                isImageExist={isExist.isImageExist}
                isFloorPlanExist={isExist.isFloorPlanExist}
                open={open}
                handleClose={handleClose}
                setOpen={setOpen}></EditSiteModal>
            <CreateAssetModal
                openAsset={assetOpen}
                handleAssetChange={handleAssetInputChange}
                handleAssetClose={handleAssetClose}
                handleAssetCreate={handleAssetCreate}
                handleChangeAssetType={handleChangeAssetType}
                handleChangeAssetArea={handleChangeAssetArea}
                assetAreas={assetAreas}
                assetTypes={assetTypes}
                setAssetAreas={setAssetAreas}
                setAssetTypes={setAssetTypes}></CreateAssetModal>
            <CreateMonitoringDeviceModal
                openMonitoringDevice={monitoringDeviceOpen}
                handleMonitoringDeviceChange={handleMonitoringDeviceInputChange}
                handleMonitoringDeviceClose={handleMonitoringDeviceClose}
                handleMonitoringDeviceCreate={handleMonitoringDeviceCreate}
                handleChangeMonitoringDeviceType={handleChangeMonitoringDeviceType}
                handleChangeMonitoringDeviceArea={handleChangeMonitoringDeviceArea}
                monitoringDeviceAreas={monitoringDeviceAreas}
                monitoringDeviceTypes={monitoringDeviceTypes}
                setMonitoringDeviceAreas={setMonitoringDeviceAreas}
                setMonitoringDeviceTypes={setMonitoringDeviceTypes}
                isMonitoringDeviceNameExist={isExist.isMonitoringDeviceNameExist}></CreateMonitoringDeviceModal>
        </TwinLayout>
    )
}

const mapStateToProps = (state: any) => ({ ...state })

const mapDispatchToProps = (dispatch: React.Dispatch<any>) => ({
    updateSelected: (asset: Asset) => dispatch(setSelectedAsset(asset)),
    updateSelectedSite: (site: Site) => dispatch(setSelectedSite(site)),
})

export default connect(mapStateToProps, mapDispatchToProps)(SiteDetail)

这是模态的:

import React from 'react'
import { Avatar, Button, Chip, Link, List, ListItem, ListItemAvatar, ListItemButton, ListItemText, Typography } from '@mui/material'
import { deepPurple } from '@mui/material/colors'
import { Asset, MeasuringPoint, MonitoredComponent } from '../types/entities'
import { connect } from 'react-redux'
import { setSelectedAsset } from '../actions'
import SpreadOperatorButton from './SpreadOperatorButton'

function ListAvatarData(prop: { handleClick?: ((item: Asset) => void) | ((item: MeasuringPoint) => void); setDelete: any; data: any; type: string; fetchData: any }) {
    const { data, type } = prop

    const handleClick = (item: any) => {
        if (prop.handleClick) prop.handleClick(item)
    }

    const getAvatarTitle = (item: any) => {
        if (type === 'Assets') return item.type.name.at(0)
        if (type === 'Components') return item.name.at(0)
        return item.type.at(0)
    }

    const getItemText = (item: any) => {
        if (type === 'MeasuringPoints' && item.monitoredComponents.length)
            return (
                <div>
                    {item.monitoredComponents.map((mc: MonitoredComponent) => {
                        return <Chip key={mc.id} className="chip" color="primary" label={mc.name}></Chip>
                    })}
                </div>
            )
        if (type === 'Components' && item.serialNumber)
            return (
                <div>
                    <Typography fontSize={14} fontWeight={400}>
                        {item?.manufacturer} - {item?.model}
                    </Typography>
                    <Typography fontSize={14} fontWeight={400}>
                        {item?.serialNumber}
                    </Typography>
                </div>
            )
        return null
    }

    return (
        <div>
            {data?.map((item: any, index: number) => {
                return (
                    <List sx={{ width: '100%', bgcolor: 'background.paper' }}>
                        <ListItem key={item.id}>
                            {type !== 'MeasuringPoints' && (
                                <ListItemAvatar>
                                    <Link href="#">
                                        <Avatar sx={{ bgcolor: deepPurple[500] }}>{getAvatarTitle(item)}</Avatar>
                                    </Link>
                                </ListItemAvatar>
                            )}
                            <div>
                                <Button onClick={() => handleClick(item)} sx={{ textAlign: 'left' }}>
                                    <ListItemText
                                        aria-multiline
                                        primary={
                                            <Typography fontWeight={600} fontSize={16} color={'#1B1B1F'}>
                                                {type === 'MeasuringPoints' ? item.location : item.name}
                                            </Typography>
                                        }
                                        secondary={type == 'Assets' ? item.type.name : item.type}
                                    />
                                </Button>
                                {getItemText(item)}
                            </div>
                            <ListItemButton className="flex" sx={{ justifyContent: 'flex-end' }}>
                                <SpreadOperatorButton fetchData={prop.fetchData} data={item} listType={prop.type} setDelete={prop.setDelete}></SpreadOperatorButton>
                            </ListItemButton>
                        </ListItem>
                    </List>
                )
            })}
        </div>
    )
}

const mapStateToProps = (state: any) => ({ ...state })

const mapDispatchToProps = (dispatch: React.Dispatch<any>) => ({
    updateSelected: (asset: Asset) => dispatch(setSelectedAsset(asset)),
})

export default connect(mapStateToProps, mapDispatchToProps)(ListAvatarData)

当我在React.useEffect()中使用if(deleteItem)语句时,ListAvatarData组件中的选择框没有返回任何项。当我删除if(deleteItem)语句并直接使用下面的useEffect时,它没有返回任何问题。

React.useEffect(() => {
    if (deleteItem) fetchData()
}, [deleteItem])

我不明白这种行为的原因,但任何回答都是非常感谢的。谢谢!

nfs0ujit

nfs0ujit1#

在代码的什么地方将deleteItem设置为true?
我觉得应该是

const handleDeleteItem = () => {
    setDeleteItem(true);
}

当状态deleteItem发生变化时,将触发以[deletItem]作为依赖项的React.useEffect。如果您只使用React.useEffect(),则仅在第一次渲染时触发。
UseEffect文档React

相关问题