【问题标题】:I am changing the state, but subscribed components are not rendered我正在更改状态,但未呈现订阅的组件
【发布时间】:2021-02-23 08:48:37
【问题描述】:

我找到元素的索引号,更改该索引中的值以免破坏数组中的顺序,将其作为新数组分派但未呈现订阅的组件。

     var userPhotoIndex = userPhotos.findIndex(p => p.photoId === photoId) 
    
     if (userPhotoIndex > -1) {
        userPhotos[userPhotoIndex].likeCount -= 1;
        dispatch(getUserPhotosSuccess([...userPhotos]))
      }

其实redux扩展的状态会发生变化,而订阅的组件没有变化。

挂钩

function LikeButton({ photo, photoId, setlikeCount }) {
const [isLike, setIsLike] = useState(false)
const dispatch = useDispatch()
const history = useHistory()
const isLogged = useSelector(state => state.isLoggedReducer);
const userPhotos = useSelector(state => state.userReducer.userPhotos);

const onClick = () => {
    var fd = new FormData();
    fd.append("photoId", photoId)
    if (!isLike) {
        axios.post(LIKE_API_URL, fd, { headers: authHeaderObj() }).then(() => {
            setIsLike(!isLike);
            setlikeCount(!isLike);
            var userPhotoIndex = userPhotos.findIndex(p => p.photoId === photoId)
            if (history.location.pathname.includes("me/" + profileFlowState.Likes)) {
                if (userPhotoIndex > -1) {
                    userPhotos[userPhotoIndex].likeCount += 1;
                    dispatch(getUserPhotosSuccess([...userPhotos]))
                }
            }

        }).catch(err => redirectErrPage(err, dispatch));
    }
    else {
        axios.delete(deleteLikePath(photoId), { headers: authHeaderObj() }).then(() => {
            setIsLike(!isLike);
            setlikeCount(!isLike);
            var userPhotoIndex = userPhotos.findIndex(p => p.photoId === photoId)
            if (history.location.pathname.includes("me/" + profileFlowState.Likes)) {
                if (userPhotoIndex > -1) {
                    userPhotos[userPhotoIndex].likeCount -= 1;
                    dispatch(getUserPhotosSuccess([...userPhotos]))
                }
            }
        }).catch(err => redirectErrPage(err, dispatch));
    }

}

useEffect(() => {
    if (isLogged) {
        axios.get(getIsLikePath(photoId), { headers: authHeaderObj() }).then(res => setIsLike(res.data))
    }
}, [isLogged, photoId])
if (!isLogged) {
    return <Button onClick={() => history.push("/login")} variant="outline-primary" style={{ borderRadius: 0 }} className="btn-sm">
        <i className="fa  fa-thumbs-up" style={{ fontSize: 16 }}></i>&nbsp;&nbsp;Beğen</Button>
}
return (
    <Button onClick={onClick} variant={isLike ? "primary" : "outline-primary"} style={{ borderRadius: 0 }} className="btn-sm">
        <i className="fa  fa-thumbs-up" style={{ fontSize: 16 }}></i>&nbsp;&nbsp;Beğen</Button>
)}

下面我正在渲染一个订阅的钩子。

已订阅

function UserPhotos({ userId }) {
const dispatch = useDispatch();

const [isLoading, setIsLoading] = useState(true)
const setFalseIsLoading = () => setIsLoading(false);
useEffect(() => { dispatch(getUserPhotosApi(userId, setFalseIsLoading)); }, [userId, dispatch])

const userPhotos = useSelector(state => state.userReducer.userPhotos)

return <div className="mt-3">{isLoading ? <Loading /> : <div>
    <MapPhotoCard removeButton={true} refreshPhotos={(id) => {
        dispatch(getUserPhotosSuccess([...userPhotos.filter(p => p.photoId !== id)]))
    }} photos={userPhotos} /></div>}
</div>}

【问题讨论】:

  • 可以分享完整的组件代码吗? userPhotos 是状态变量吗?
  • 是的,我正在获取当前状态const userPhotos = useSelector(state =&gt; state.userReducer.userPhotos);
  • 我需要完整的组件代码来提供帮助。首先,我可以说你永远不应该改变你的状态变量而不像userPhotos[userPhotoIndex].likeCount -= 1;那样调度一个动作,这是有问题的。如果需要,您可以更新您的问题以包含更多代码
  • 但我将它作为一个新数组发送。你不认为它应该改变吗?
  • @AliYıldızöz 你改变了组件中的 redux 状态,然后用改变的值调度一个动作。您应该从减速器返回一个新状态,here 是一些信息,您可以如何做到这一点。

标签: javascript arrays reactjs redux react-redux


【解决方案1】:

问题:改变状态

复制太晚

dispatch(getUserPhotosSuccess([...userPhotos]))

您正在使用数组的副本分派您的操作,但那是您已经改变了状态,所以它没有帮助。

浅拷贝

您的数组包含一堆对象,但在幕后您的数组包含对象的引用。当您使用[...userPhotos] 克隆数组时,您会得到一个包含所有相同对象引用的新数组。因此,当您在其中一个对象上设置likeCount 之类的属性时,您也在为处于 redux 状态的该对象设置该属性。

解决方案:新数组中的新对象

为了避免突变,我们必须为您正在更新的照片返回一个包含新对象的新数组。不需要或不希望对整个数组执行深层复制。您未更改的照片可以保持不变。深拷贝会导致对未更改的数据进行不必要的重新渲染。

这里的common solution 是使用Array.prototype.map()。对于与photoId 匹配的照片,我们返回一个新的照片对象。数组的所有其他元素保持不变。

const newPhotos = userPhotos.map((photo) =>
  photo.photoId === photoId
    ? {
        ...photo,
        likeCount: photo.likeCount + 1
      }
    : photo
);
dispatch(getUserPhotosSuccess(newPhotos));

建议:更具体的行动

这种更新通常在 reducer 中完成,而不是在组件中。为什么必须返回包含每张照片的数组来响应“喜欢”或“不喜欢”操作?

我建议使用尽可能少的数据来调度操作。然后在您的减速器中,您可以应用这些更改。根据您的状态的数据结构,您可能需要知道用户才能更新该用户的照片,或者您可能只需要知道照片 ID。

在这里,我们传递照片的更改属性。 reducer 会将它们与现有属性结合起来。这个动作很好,因为它有很多用途,但仍然需要很少的数据。

{ type: "UPDATE_PHOTO", payload: { photoId: _id_, changes: { likeCount: _newCount_ } }

你可以把喜欢变成自己的行为。您可以在 reducer 中分别处理类似和不同的单独操作。这不是我个人的最爱。

{ type: "LIKE_PHOTO", payload: { photoId: _id_ } }
{ type: "UNLIKE_PHOTO", payload: { photoId: _id_ } }

如果您使用属性change 的值为1-1 的like 和like 共享操作,reducer 中的实现会容易得多。通过将change 添加到现有的like 计数,reducer 以相同的方式处理这两种方法。您可以将其与动作创建器函数结合使用以实现分离。

({ type: "UPDATE_LIKES", payload: { photoId: _id_, change: 1 } })
const likePhoto = (photoId) => ({ type: "UPDATE_LIKES", payload: { photoId, change: 1 } })
const unikePhoto = (photoId) => ({ type: "UPDATE_LIKES", payload: { photoId, change: -1 } })

【讨论】:

    【解决方案2】:

    有一个重要的问题要提一下:

    改变 redux 状态


    在这种情况下,您有一个状态变量,即userPhotos,它位于 redux 存储中。 userPhotos 是数组类型的变量,通过userPhotos[userPhotoIndex].likeCount -= 1; 行更改likeCount 是一种反模式。理想情况下,您希望首先使用以下两种方法之一克隆数组:

    使用拆分运算符

    const userPhotosCopy = [...userPhotos]
    userPhotosCopy[userPhotoIndex].likeCount -= 1
    /*or userPhotosCopy.splice(userPhotoIndex, 1, {...userPhotosCopy[userPhotoIndex], likeCount: likeCount - 1 }]*/
    

    使用 lodash

    import _ from 'lodash'
    const userPhotosCopy = _.cloneDeep(userPhotos)
    

    在 useEffect 上获取

    在订阅组件中更新userPhotos 后,理想情况下,您需要这样的模式:

    const userPhotos = useSelector(state => state.userReducer.userPhotos);
    
    return(
       // use userPhotos in whatever way you like
    )
    

    不确定 getUserPhotosSuccess 操作在做什么,但您似乎没有在订阅的组件中获得更新后的 userPhotos 对象

    【讨论】:

    • 感谢您的回答。是的。完全正确。当我更改 photoUrlusernamelikeCount 不会更改时,状态会更改。我认为这是由其他原因引起的。我在这里使用userphotos photos={userPhotos}
    • 是的,你可以尝试使用 userPhotos.map(photo => ... 来渲染你的照片,如果这是你想要的
    猜你喜欢
    • 2023-03-28
    • 1970-01-01
    • 2021-10-16
    • 2017-09-18
    • 2021-12-21
    • 1970-01-01
    • 1970-01-01
    • 2021-02-08
    • 1970-01-01
    相关资源
    最近更新 更多