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

时间:2020-11-04 03:51:22

标签: javascript reactjs react-redux react-bootstrap

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

import React, { useState, useEffect, setState } from 'react';
import { Form, Button, Row, Col, Tabs, Tab } from 'react-bootstrap';
import { LinkContainer } from 'react-router-bootstrap';
import { useDispatch, useSelector } from 'react-redux';
import { getChefDetails, updateChefProfile } from '../../actions/chefActions';
import { CHEF_UPDATE_PROFILE_RESET } from '../../constants/chefConstants';
import FormContainer from '../../components/FormContainer/FormContainer.component';

import './ProfileEditPage.styles.scss';

const ProfileEditPage = ({ location, history }) => {
  const [first_name, setFirstName] = useState('')
  const [last_name, setLastName] = useState('')
  const [username, setUsername] = useState('')
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')
  const [confirmPassword, setConfirmPassword] = useState('')
  const [isVegan, setIsVegan] = useState('')
  const [bio, setBio] = useState('')
  const [message, setMessage] = useState(null)

  const dispatch = useDispatch()

  const chefDetails = useSelector(state => state.chefDetails)
  const { loading, error, chef } = chefDetails

  const chefLogin = useSelector(state => state.chefLogin)
  const { chefInfo } = chefLogin

  const chefUpdateProfile = useSelector(state => state.chefUpdateProfile)
  const { success } = chefUpdateProfile

  useEffect(() => {
    if(!chefInfo) {
      history.push('/login')
    } else {
      if(!chef || !chef.username || success) {
        dispatch({ type: CHEF_UPDATE_PROFILE_RESET })
        dispatch(getChefDetails('profile'))
      } else {
        setFirstName(chef.first_name)
        setLastName(chef.last_name)
        setUsername(chef.username)
        setEmail(chef.email)
        setBio(chef.bio)
        setIsVegan(chef.isVegan)
      }
    }
  }, [dispatch, history, chefInfo, chef, success])

  const submitHandler = (e) => {
    e.preventDefault()
    if (password !== confirmPassword) {
      setMessage('Passwords do not match')
    } else {
      dispatch(updateChefProfile({
        id: chef._id,
        first_name,
        last_name,
        username,
        email,
        password,
        bio,
        isVegan
      }))
    }
  }

  const [key, setKey] = useState('auth')

  //const isVegan = chef.diets[0].isVegan
  //const isVegetarian = chef.diets[0].isVegetarian

  console.log(isVegan)

  return (
    <FormContainer className="profileEditPage">
      <h1>Chef Profile</h1>
      <Form className='profileEditPageForm' onSubmit={submitHandler}>
        <Tabs id="profileEditPageTabs" activeKey={key} onSelect={(k) => setKey(k)}>
          <Tab eventKey='auth' title="Auth">
            <Form.Group controlId='first_name'>
              <Form.Label>First Name</Form.Label>
              <Form.Control
                type='text'
                placeholder='Enter your first name'
                value={first_name}
                onChange={(e) => setFirstName(e.target.value)}
                required
              >
              </Form.Control>
            </Form.Group>

            <Form.Group controlId='last_name'>
              <Form.Label>Last Name</Form.Label>
              <Form.Control
                type='text'
                placeholder='Enter your last name'
                value={last_name}
                onChange={(e) => setLastName(e.target.value)}
                required
              >
              </Form.Control>
            </Form.Group>

            <Form.Group controlId='username'>
              <Form.Label>Username</Form.Label>
              <Form.Control
                type='text'
                placeholder='Enter a username'
                value={username}
                onChange={(e) => setUsername(e.target.value)}
                required
              >
              </Form.Control>
              <Form.Text className='muted'>Your username will be public</Form.Text>
            </Form.Group>

            <Form.Group controlId='email'>
              <Form.Label>Email</Form.Label>
              <Form.Control
                type='email'
                placeholder='Enter your email'
                value={email}
                onChange={(e) => setEmail(e.target.value)}
                required
              >
              </Form.Control>
            </Form.Group>

            <Form.Group controlId='password'>
              <Form.Label>Password</Form.Label>
              <Form.Control
                type='password'
                placeholder='Enter your password'
                value={password}
                onChange={(e) => setPassword(e.target.value)}
              >
              </Form.Control>
            </Form.Group>

            <Form.Group controlId='confirmPassword'>
              <Form.Label>Confirm Password</Form.Label>
              <Form.Control
                type='password'
                placeholder='Confirm password'
                value={confirmPassword}
                onChange={(e) => setConfirmPassword(e.target.value)}
              >
              </Form.Control>
            </Form.Group>
          </Tab>
          <Tab eventKey='chef-detail' title="Chef Detail">
            <Form.Group controlId='isVegan'>
              <Form.Check
                type='checkbox'
                label='Vegan?'
                checked={isVegan}
                value={isVegan}
                onChange={(e) => setIsVegan(e.target.checked)}
              />
            </Form.Group>

            <Form.Group controlId='bio'>
              <Form.Label>Chef Bio</Form.Label>
              <Form.Control
                as='textarea'
                rows='5'
                maxLength='240'
                placeholder='Enter bio'
                value={bio}
                onChange={(e) => setBio(e.target.value)}
              >
              </Form.Control>
              <Form.Text className='muted'>Your bio will be public</Form.Text>
            </Form.Group>
          </Tab>
        </Tabs>

        <Button type='submit' variant='primary'>
          Update
        </Button>
      </Form>


    </FormContainer>

  )
}

export default ProfileEditPage;

动作

export const getChefDetails = (id) => async (dispatch, getState) => {
  try {
    dispatch({
      type: CHEF_DETAILS_REQUEST
    })

    const { chefLogin: { chefInfo} } = getState()

    const config = {
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${chefInfo.token}`
      }
    }

    const { data } = await axios.get(
      `/api/chefs/${id}`,
      config
    )

    dispatch({
      type: CHEF_DETAILS_SUCCESS,
      payload: data
    })

  } catch (error) {
    dispatch({
      type: CHEF_DETAILS_FAILURE,
      payload:
        error.response && error.response.data.message
          ? error.response.data.message
          : error.message,
    })
  }
}

export const updateChefProfile = (chef) => async (dispatch, getState) => {
  try {
    dispatch({
      type: CHEF_UPDATE_PROFILE_REQUEST
    })

    const { chefLogin: { chefInfo } } = getState()

    const config = {
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${chefInfo.token}`
      }
    }

    const { data } = await axios.put(
      `/api/chefs/profile`,
      chef,
      config
    )

    dispatch({
      type: CHEF_UPDATE_PROFILE_SUCCESS,
      payload: data
    })

    dispatch({
      type: CHEF_LOGIN_SUCCESS,
      payload: data
    })

    localStorage.setItem('chefInfo', JSON.stringify(data))

  } catch (error) {
    dispatch({
      type: CHEF_UPDATE_PROFILE_FAILURE,
      payload:
        error.response && error.response.data.message
          ? error.response.data.message
          : error.message,
    })
  }
}

减速器

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')
  }
})

1 个答案:

答案 0 :(得分:0)

问题

经过很多次来回,我认为问题在于减速器如何将响应“有效负载”存储回状态。响应对象是一个平面对象,其根为Thing,但在状态isVegan中处于嵌套的isVegan数组中。

diets

Reducer获取有效负载,并将其直接保存到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

解决方案

Reducer应该在响应有效负载中合并。在您的redux屏幕截图中,我没有看到export const chefUpdateProfileReducer = (state = { }, action) => { switch(action.type) { ... case CHEF_UPDATE_PROFILE_SUCCESS: return { loading: false, success: true, chefInfo: action.payload } ... } } 键,因此我将其编写为与屏幕截图尽可能匹配。

chefInfo

注意:这是陈述结构和类型的最佳猜测,因为您的化合器似乎具有非常小的定义的初始状态,因此可能需要进行调整以适合您的确切状态结构。