[英]Changing boolean value with React Bootstrap checkbox and hooks
尝试使用 React Bootstrap 复选框和钩子更改 isVegan 对象(嵌套布尔值)。 我可以毫无问题地访问该对象(例如,如果 isVegan 为 true,则选中复选框),但无法修改状态。 正如您在 Redux 开发工具(包括图像链接)中所见,isVegan 对象通过我的状态传递并且是可访问的。 我还对厨师集合中的其他对象使用了类似的代码,没有任何问题,因此相信问题要么与复选框有关,要么与 isVegan 对象如何嵌套在厨师集合中有关。 (最后,我知道下面的一些代码可能是额外的,我缩小了我的原始文件以简化这个例子)
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')
}
})
经过多次来回,我认为问题在于reducer如何将响应“有效负载”存储回状态。 响应对象是一个以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 }
...
}
}
Reducer 应该在响应负载中合并。 在您的 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),
},
};
...
}
}
注意:这是对状态结构和类型的最佳猜测,因为您的 reducer 似乎具有非常低定义的初始状态,因此可能需要对其进行调整以适合您的确切状态结构。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.