Javascript 使用React Bootstrap复选框和挂钩更改布尔值

Javascript 使用React Bootstrap复选框和挂钩更改布尔值,javascript,reactjs,react-redux,react-bootstrap,Javascript,Reactjs,React Redux,React Bootstrap,尝试使用React Bootstrap复选框和挂钩更改isVegan对象(嵌套布尔值)。我可以访问对象而没有任何问题(例如,如果isVegan为true,则选中复选框),但无法修改状态。正如您在Redux开发工具(包括图像链接)中所看到的,isVegan对象通过my state传递并且是可访问的。我还为chef集合中的其他对象使用了类似的代码,没有任何问题,因此请相信问题要么与复选框有关,要么与isVegan对象如何嵌套在chef集合中有关。(最后,我知道下面的一些代码可能是额外的,我精简了原始

尝试使用React Bootstrap复选框和挂钩更改isVegan对象(嵌套布尔值)。我可以访问对象而没有任何问题(例如,如果isVegan为true,则选中复选框),但无法修改状态。正如您在Redux开发工具(包括图像链接)中所看到的,isVegan对象通过my state传递并且是可访问的。我还为chef集合中的其他对象使用了类似的代码,没有任何问题,因此请相信问题要么与复选框有关,要么与isVegan对象如何嵌套在chef集合中有关。(最后,我知道下面的一些代码可能是额外的,我精简了原始文件以简化此示例)

还原剂

export const chefDetailsReducer = (state = { chef: { } }, action) => {
  switch(action.type) {
    case CHEF_DETAILS_REQUEST:
      return { ...state, loading: true }
    case CHEF_DETAILS_SUCCESS:
      return { loading: false, chef: action.payload }
    case CHEF_DETAILS_FAILURE:
      return { loading: false, error: action.payload }
    case CHEF_DETAILS_RESET:
      return {
        chef: {}
      }
    default:
      return state
  }
}

export const chefUpdateProfileReducer = (state = { }, action) => {
  switch(action.type) {
    case CHEF_UPDATE_PROFILE_REQUEST:
      return { loading: true }
    case CHEF_UPDATE_PROFILE_SUCCESS:
      return { loading: false, success: true, chefInfo: action.payload }
    case CHEF_UPDATE_PROFILE_FAILURE:
      return { loading: false, error: action.payload }
    case CHEF_UPDATE_PROFILE_RESET:
      return { }
    default:
      return state
  }
}
控制器

// @description Get chef profile
// @route GET /api/chefs/profile
// @access Private
const getChefProfile = asyncHandler(async (req, res) => {
  const chef = await Chef.findById(req.chef._id)

  if(chef) {
    res.json({
      _id: chef._id,
      first_name: chef.first_name,
      last_name: chef.last_name,
      username: chef.username,
      email: chef.email,
      bio: chef.bio,
      isVegan: chef.isVegan
    })
  } else {
    res.status(404)
    throw new Error('Chef not found')
  }
})

// @description Update chef profile
// @route PUT /api/chefs/profile
// @access Private
const updateChefProfile = asyncHandler(async (req, res) => {
  const chef = await Chef.findById(req.chef._id)

  if(chef) {
    chef.first_name = req.body.first_name || chef.first_name
    chef.last_name = req.body.last_name || chef.last_name
    chef.username = req.body.username || chef.username
    chef.email = req.body.email || chef.email
    chef.bio = req.body.bio || chef.bio
    chef.isVegan = req.body.isVegan || chef.isVegan

    if (req.body.password) {
      chef.password = req.body.password
    }

    const updatedChef = await chef.save()

    res.json({
      _id: updatedChef._id,
      first_name: updatedChef.first_name,
      last_name: updatedChef.last_name,
      username: updatedChef.username,
      email: updatedChef.email,
      bio: updatedChef.bio,
      isVegan: updatedChef.isVegan,
      token: generateToken(updatedChef._id),
    })

  } else {
    res.status(404)
    throw new Error('Chef not found')
  }
})
问题 经过多次反复,我认为问题在于减速器如何将响应“有效负载”存储回状态。响应对象是一个平面对象,根位置为
isVegan
,但在状态下
isVegan
位于嵌套的
diets
数组中

res.json({
  _id: updatedChef._id,
  first_name: updatedChef.first_name,
  last_name: updatedChef.last_name,
  username: updatedChef.username,
  email: updatedChef.email,
  bio: updatedChef.bio,
  isVegan: updatedChef.isVegan,
  token: generateToken(updatedChef._id),
})
减速机获取有效负载,并将其直接保存到
chefInfo
属性,并覆盖任何现有数据

export const chefUpdateProfileReducer = (state = { }, action) => {
  switch(action.type) {
    ...

    case CHEF_UPDATE_PROFILE_SUCCESS:
      return { loading: false, success: true, chefInfo: action.payload }

    ...
  }
}
解决方案 减速器应合并以响应有效负载。在您的redux屏幕截图中,我没有看到
chefInfo
键,因此我将编写此代码以尽可能与屏幕截图匹配

export const chefUpdateProfileReducer = (state = { }, action) => {
  switch(action.type) {
    ...

    case CHEF_UPDATE_PROFILE_SUCCESS:
      const {
        _id,
        isVegan,
        token,
        ...chefDetails // i.e. first & last name, username, email, bio
      } = action.payload;

      return {
        ...state, // <-- shallow copy state
        loading: false,
        success: true,
        chef: {
          ...state.chef, // <-- shallow copy existing chef details
          ...chefDetails, // shallow copy new chef details
          diets: state.chef.diets.map(diet => diet._id === _id ? { // <-- map existing state
            ...diet, // <-- shallow copy diet object
            isVegan // <-- overwrite isVegan property
          } : diet),
        },
      };

    ...
  }
}
export const chefUpdateProfileReducer=(state={},action)=>{
开关(动作类型){
...
案例厨师长\u更新\u档案\u成功:
常数{
_身份证,
我是素食主义者,
代币
…支票详情//即姓名、用户名、电子邮件、简历
}=动作有效载荷;
返回{

…状态,//在共享的代码段中,
isVegan
是本地组件状态的一部分,而不是您的redux状态。存储/复制状态是react反模式。哪个状态没有更新?@drewerese感谢您的评论并花时间帮助。不幸的是,我不确定我是否理解您的问题,因为我能够更新这样的对象作为“first_name”,而不是“diets”(最终存储“isVegan”)。例如,如果我虚拟地模仿“first_name”的“isVegan”设置(稍作调整),然后在表单中使用一个函数(例如“onChange={(e)=>setFirstName(e.target.value)})我的状态在本地和数据库中更新。所以我想我的后续问题是,我需要在后端做些什么吗?可能在控制器内部?你有
isVegan
本地组件状态。你的redux状态中也有一些
isVegan
。你不能修改哪个状态?你是说你的
updateChefPro吗文件
action没有更新redux存储?你能包含你的actions和reducer吗?更新为包含actions和reducer。我确认,当我选中并取消选中复选框时,isVegan的本地状态在true和false之间切换,因此理解所说的“isVegan”是本地组件状态。在
updateChefProfil中e
您是否验证了
数据
是否符合您对PUT请求的预期?是否检查了响应?是否安装了redux devtool,以便您可以从浏览器的devtool监控存储?是否按预期调度了正确的操作(和有效负载)?感谢此解决方案。我收到一个错误意外标记,应为“,”与上一个标记相关{"在映射函数的行中。@jasontulloch可能您的IDE没有设置/配置尾随逗号,您可以删除它。我出于习惯键入它们。让我在IDE中运行它来确定。哎呀,这是一个三元操作,我只是忘记了代码中添加的三元
。对不起。@jasontulloch就是这样。一切都好了在
{}
中是声明的变量。你在ES6 JS上吗?肯定有帮助!现在我得到了一个“无法访问的代码”错误,它似乎与失败和重置常量有关。我在ES6上。@jasontulloch我刚刚将reducer函数从我的解决方案复制/粘贴到了一个codesandbox中,并在所有其他情况下添加回来,然后(除了未定义的所有动作类型/etc)这是正确的,它可以到达后续动作案例。可能您错过了一个结束曲男孩
}
export const chefUpdateProfileReducer = (state = { }, action) => {
  switch(action.type) {
    ...

    case CHEF_UPDATE_PROFILE_SUCCESS:
      return { loading: false, success: true, chefInfo: action.payload }

    ...
  }
}
export const chefUpdateProfileReducer = (state = { }, action) => {
  switch(action.type) {
    ...

    case CHEF_UPDATE_PROFILE_SUCCESS:
      const {
        _id,
        isVegan,
        token,
        ...chefDetails // i.e. first & last name, username, email, bio
      } = action.payload;

      return {
        ...state, // <-- shallow copy state
        loading: false,
        success: true,
        chef: {
          ...state.chef, // <-- shallow copy existing chef details
          ...chefDetails, // shallow copy new chef details
          diets: state.chef.diets.map(diet => diet._id === _id ? { // <-- map existing state
            ...diet, // <-- shallow copy diet object
            isVegan // <-- overwrite isVegan property
          } : diet),
        },
      };

    ...
  }
}