首页 > 解决方案 > 单击多个相同组件之一后,仅发出一个具有新状态的发布请求

问题描述

我有一个小问题,不知道如何解决它。希望你能帮忙

我创建了一个简单的 Starrating 组件。你有五颗星。如果您单击其中一颗星星,状态会发生变化,依此类推……(5 颗星。评级从 1 到 5:D)。只是基本的东西。

主要问题是基于 Starrating 组件是另一个组件(AlbumList.js)的一部分,该组件在主页上呈现 5 次(您可以评价 5 张不同的图片)(之间还有另一个组件 AlbumCard.js它持有 Starrating 组件,但我认为这并不重要。

基本上我有 5 个相同的组件,每个组件都有 Starrating 组件。

我的主要目标是单击其中一张图片,对其进行评分并将正确的状态发送到我的数据库。 在 useEffect 中调用向数据库发送正确评分的函数 (rateAlbum),因为只有在那里我才能将新状态发送到我的数据库(在 useEffect 之外我只能在渲染后访问新状态,我猜)。

不幸的是,如果我重新加载页面或只设置一个速率,则调用该函数的次数与图片的次数一样多(5 次)

如果我只对一张图片进行评分,或者在 onClick 右图的新状态之后,如何调用该函数?

Starrating.js

import React, { useEffect, useState } from 'react'
import { FaStar } from 'react-icons/fa'
import { rateAlbum } from '../../store/actions/userAlbumRatingAction'

function Starrating({ width }) {
  const [rating, setRating] = useState(null)
  const [hover, setHover] = useState(null)

  const ratePicture = (rating) => {
    setRating(rating)
//  ratePicture() do not have the new state of rating
  }

    useEffect(() => {
    rateAlbum({ // function which is making the axios call
//... not imporant information just the right IDs and so on 
      rating: rating,
   
    })
  }, [rating])



  return (
    <div className='flex h-full' style={{ width: width }}>
      {[...Array(5)].map((star, i) => {
        const ratinValue = i + 1
        return (
          <label key={ratinValue} className='flex items-center w-full'>
            <input
              className='hidden'
              type='radio'
              name='raiting'
              value={ratinValue}
              onClick={
                () => ratePicture(ratinValue)
                //  () => setRating(ratinValue)
              }
            />
            <FaStar
              className='md:m-1 w-full h-full delay-200 cursor-pointer'
              color={ratinValue <= (hover || rating) ? '#ffc107' : '#e4e5e9'}
              onMouseEnter={() => setHover(ratinValue)}
              onMouseLeave={() => setHover(null)}
            />
          </label>
        )
      })}
    </div>
  )
}


 export default Starrating

AlbumList.js(起始组件是 AlbumCard 组件的一部分)

   import { connect, useSelector } from 'react-redux'
import { fetchAlbum } from '../../store/actions/albumAction'

import AlbumCard from './AlbumCard'

import { setView } from '../../store/actions/uiAction'
import { useHistory } from 'react-router'

function AlbumList(props) {
  const newreleases = useSelector((state) => state.newReleases.NewReleases)
  const view = useSelector((state) => state.ui.view)
  const searchAlbum = useSelector((state) => state.search.albums)

  const history = useHistory()

  const onAlbumCardClick = (dataId) => {
    props.fetchAlbum(dataId)
    history.push('/home/album')
  }

  return (
    <section className='sm:flex sm:justify-between sm:flex-nowrap grid grid-cols-3'>
      {view === 'noSearch' ? (
        <>
          {newreleases.slice(0, 5).map((data) => (
            <AlbumCard
              url={data.images[0].url}
              key={data.id}
              id={data.id}
              albumname={data.name}
              onClick={() => onAlbumCardClick(data.id)}
            />
          ))}
        </>
      ) : view === 'search' ? (
        <>
          {searchAlbum.slice(0, 5).map((data, index) => (
            <AlbumCard
              url={data.images[0].url}
              key={data.id}
              id={data.id}
              albumname={data.name}
              onClick={() => onAlbumCardClick(data.id)}
            />
          ))}
        </>
      ) : null}
    </section>
  )
}

const mapDispatch = { fetchAlbum, setView }
export default connect(null, mapDispatch)(AlbumList)

rateAlbum 功能

export const rateAlbum = (data) => {
  axios.post('....', data)
}

AlbumCard.js(不重要,但有启动组件,并且 Albumcard.js 是 ALbumList.js 的一部分)

import React from 'react'
import CardButtons from './CardButtons'
import Starrating from '../HelperComponents/Starrating'

    function AlbumCard({ url, albumname, onClick }) {
  

  return (
    <>
      <div className=' sm:m-2 sm:w-40 dark:bg-white w-24 m-1 rounded-lg shadow-md'>
        <div onClick={onClick} id='hi' className='group relative rounded-lg'>
          <img
            className='md:w-72 block w-full h-full rounded-lg'
            src={url}
            alt=''
          />

          <div className='group-hover:bg-opacity-60 group-hover:opacity-100 justify-evenly absolute top-0 flex items-center w-full h-full transition bg-black bg-opacity-0 rounded-md'>
            <CardButtons />
          </div>
        </div>
        <div className=' flex flex-col items-center justify-center pt-3 pb-3'>
          <p className='dark:text-black font-body whitespace-nowrap flex justify-center w-11/12 mb-2 overflow-hidden text-xs text-black'>
            {albumname}
          </p>
          <Starrating />
        </div>
      </div>
    </>
  )
}
export default AlbumCard

标签: reactjsaxiosreact-hooksstate

解决方案


启动评分.js

import React, { useEffect, useState } from 'react'
import { FaStar } from 'react-icons/fa'
import { rateAlbum } from '../../store/actions/userAlbumRatingAction'

function Starrating({ width, onClickStar }) {
  const [rating, setRating] = useState(null)
  const [hover, setHover] = useState(null)

  const ratePicture = (rating) => {
    setRating(rating)
//  ratePicture() do not have the new state of rating
  }

    useEffect(() => {
    rateAlbum({ // function which is making the axios call
//... not imporant information just the right IDs and so on 
      rating: rating,
   
    })
  }, [rating])



  return (
    <div className='flex h-full' style={{ width: width }}>
      {[...Array(5)].map((star, i) => {
        const ratinValue = i + 1
        return (
          <label key={ratinValue} className='flex items-center w-full'>
            <input
              className='hidden'
              type='radio'
              name='raiting'
              value={ratinValue}
              onClick={
                () => onClickStart(ratinValue) 
                //  () => setRating(ratinValue)
              }
            />
            <FaStar
              className='md:m-1 w-full h-full delay-200 cursor-pointer'
              color={ratinValue <= (hover || rating) ? '#ffc107' : '#e4e5e9'}
              onMouseEnter={() => setHover(ratinValue)}
              onMouseLeave={() => setHover(null)}
            />
          </label>
        )
      })}
    </div>
  )
}


 export default Starrating


专辑卡,js

import React from 'react'
import CardButtons from './CardButtons'
import Starrating from '../HelperComponents/Starrating'

    function AlbumCard({ url, albumname, onClick, onClickStar }) {
  

  return (
    <>
      <div className=' sm:m-2 sm:w-40 dark:bg-white w-24 m-1 rounded-lg shadow-md'>
        <div onClick={onClick} id='hi' className='group relative rounded-lg'>
          <img
            className='md:w-72 block w-full h-full rounded-lg'
            src={url}
            alt=''
          />

          <div className='group-hover:bg-opacity-60 group-hover:opacity-100 justify-evenly absolute top-0 flex items-center w-full h-full transition bg-black bg-opacity-0 rounded-md'>
            <CardButtons />
          </div>
        </div>
        <div className=' flex flex-col items-center justify-center pt-3 pb-3'>
          <p className='dark:text-black font-body whitespace-nowrap flex justify-center w-11/12 mb-2 overflow-hidden text-xs text-black'>
            {albumname}
          </p>
          <Starrating onClickStar={onClickStar} />
        </div>
      </div>
    </>
  )
}
export default AlbumCard


专辑列表.js

  import { connect, useSelector } from 'react-redux'
import { fetchAlbum } from '../../store/actions/albumAction'

import AlbumCard from './AlbumCard'

import { setView } from '../../store/actions/uiAction'
import { useHistory } from 'react-router'

function AlbumList(props) {
  const newreleases = useSelector((state) => state.newReleases.NewReleases)
  const view = useSelector((state) => state.ui.view)
  const searchAlbum = useSelector((state) => state.search.albums)


  const [releases, setReleases] = useState([])

  useEffect(() => {
      setReleases(newreleases)
}, [newreleases])

  const history = useHistory()
  
  const setRating = (ratingValue, index) => {
     let updatedReleases = [...releases]
     updatedReleases[index].rating = ratingValue // i'm assuming you have //rating field
     setReleases(updatedReleases)
    // then send this new releases to the api
  }

  const onAlbumCardClick = (dataId) => {
    props.fetchAlbum(dataId)
    history.push('/home/album')
  }

  return (
    <section className='sm:flex sm:justify-between sm:flex-nowrap grid grid-cols-3'>
      {view === 'noSearch' ? (
        <>
          {newreleases.slice(0, 5).map((data, index) => (
            <AlbumCard
              url={data.images[0].url}
              key={data.id}
              id={data.id}
              albumname={data.name}
              onClickStar={(ratingValue) => setRating(ratingValue, index)}
              onClick={() => onAlbumCardClick(data.id)}
            />
          ))}
        </>
      ) : view === 'search' ? (
        <>
          {searchAlbum.slice(0, 5).map((data, index) => (
            <AlbumCard
              url={data.images[0].url}
              key={data.id}
              id={data.id}
              onClickStar={(ratingValue) => setRating(ratingValue, index)}
              albumname={data.name}
              onClick={() => onAlbumCardClick(data.id)}
            />
          ))}
        </>
      ) : null}
    </section>
  )
}

const mapDispatch = { fetchAlbum, setView }
export default connect(null, mapDispatch)(AlbumList)

推荐阅读