尝试使用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')
}
})
答案 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
注意:这是陈述结构和类型的最佳猜测,因为您的化合器似乎具有非常小的定义的初始状态,因此可能需要进行调整以适合您的确切状态结构。