【问题标题】:How to I manually force a parent component to re-render upon an action happening in a child component?如何手动强制父组件在子组件中发生的操作时重新渲染?
【发布时间】:2021-10-01 23:03:13
【问题描述】:

我正在制作一个 react-redux 应用程序,允许用户提交和审查不同的辣酱。

在我的个人资料页面上,我正在呈现两个列表。用户提交的调味汁之一,以及用户评论过的调味汁之一。我的后端有一个级联删除,当它被删除时,它会删除所有对酱汁的评论。目前,如果用户删除酱汁,个人资料页面上的“酱汁已审查”列表不会更新以删除已删除的酱汁,尽管酱汁已从“提交的酱汁”列表中正确删除。单击时,评论列表中的酱汁是死链接,因为该酱汁已被删除。如果您手动刷新页面,死链接就会消失,一切正常。我想知道如何手动触发我的配置文件组件的重新渲染

我的想法是使用我的 Profile 组件中定义的虚拟本地状态变量,如下所示:

const [update, setUpdate] = useState(false)

然后,我会将这些变量作为道具传递给我的删除按钮组件,如下所示:

<Profile>
usersauces.map => 
     <div>
         list component stuff
        <Delete update={update} sauce={sauce}  setUpdate={setUpdate}>

然后在我的删除组件中我有一个句柄删除功能:

const DeleteSauceButton = ({sauce, update, setUpdate}) => {

const handleDelete = async (e) => {
        e.preventDefault()
        await dispatch(thunk_goDeleteSauce(sauce?.id))
        await setUpdate(!update)
        console.log("updated", update)
        history.push(`/users/${userId}`)
    }

我的理解是删除时子组件中的这种状态变化应该会导致 Profile 父组件的重新渲染,但这并没有发生。当我在设置后 console.log(update) 时,更新仍然是“假”,而不是像我期望的那样“真”。我在这里错过了什么?

在我的个人资料组件中,我使用这样的 useSelector 订阅我的状态:

const sauceSlice = useSelector(state => state.sauces)
const reviewsSlice = useSelector(state => state.reviews)

const sauces = Object.values(sauceSlice)
const reviews = Object.values(reviewsSlice)

我映射以生成我的列表的列表被过滤如下:

const userReviews = reviews?.filter( review => review?.user_id === userId)
    const userSauces = sauces?.filter( sauce => sauce?.user_id === userId)

我更新的删除酱减速器案例是:

case DELETE_SAUCE: {
        let newState = { ...state }

        delete newState[action.sauce.id]

        return { ...newState }
    }

而review reducer也是类似的:

const initialState = {}

const reviewReducer = (state = initialState, action) => {
   

     switch (action.type) {
            case LOAD_REVIEWS: {
                return { ...state, ...action.reviews }
            }
            case DELETE_REVIEW: {
                let newState = { ...state }
    
                delete newState[action.reviewId]
    
                return { ...newState }
            }
            case UPDATE_REVIEW: {
                if (!action.review) { return { ...state } }
    
                return {
                    ...state,
                    [action.review.id]: action.review
                }
            }
            default:
                return state
        }
    }

我将 goDeleteSauce thunk 更新为:

export const thunk_goDeleteSauce = (sauce) => async (dispatch) => {
    const res = await fetch(`/api/sauces/${sauce.id}`, {
        method: 'DELETE',
    })

    if (res.ok) {
        sauce.reviews.forEach((review) => dispatch(deleteReview(review.id)))
        dispatch(deleteSauce(sauce))
    }
}

审核操作如下所示:

    export const deleteReview = (reviewId) => ({
    type: DELETE_REVIEW,
    reviewId
})

【问题讨论】:

  • 您如何订阅其他组件中的评论列表?当您删除酱汁时,应该更新 redux 状态(包括评论),以便其他组件会自动重新渲染最新的更改。
  • 在 Profile 组件或 Delete 按钮组件中?
  • 在您订阅商店状态的任何组件中。您是否正在删除与thunk_goDeleteSauce 生成的操作中删除的酱汁相关的所有评论?如果没有,那就是删除酱汁时缺少的步骤。
  • 这是有道理的,肯定是错过了这一点。我认为这帮助我走上了正确的道路,我重新配置了我的动作创建器、删除酱 thunk 和减速器中的 DELETE_SAUCE 案例,以便能够接受整个酱对象而不是 id。这样我就可以访问诸如 sauce.reviews 之类的评论,并对每个评论发送一个 thunk。不幸的是,我还没有完全到那里,但这绝对有帮助
  • 如果没有可重复的示例,我真的无法提出更多建议,但听起来您现在正在考虑所有正确的部分。当您由于某种原因无法控制应用程序的某些部分时,您应该将手动重新渲染视为仅作为黑客/逃生舱口。订阅您的商店并根据其数据的更新进行渲染是正确的继续方式。

标签: reactjs react-redux react-hooks use-state


【解决方案1】:

你不应该在 useState 中使用 await:

const handleDelete = async (e) => {
        e.preventDefault()
        await dispatch(thunk_goDeleteSauce(sauce?.id))
        setUpdate(value=>!value)
    }

【讨论】:

  • setState 调度操作是同步方法,因此使用 await 在异步上下文中无效。
  • 删除了等待,但不幸的是没有解决它
猜你喜欢
  • 1970-01-01
  • 2021-12-01
  • 2019-06-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-12
  • 2016-12-31
  • 1970-01-01
  • 2020-01-20
相关资源
最近更新 更多