当需要选中“全选”复选框时,我需要选择/取消选择所有复选框的功能,而当取消选中任何一个或多个复选框时,则需要取消选择“全选”复选框的功能。
我需要使用final-form来执行此操作(因为如果使用状态,它会重新加载视图并重置表单中的其他字段)。到目前为止,我发现最好的解决方案是使用final-form-calculate装饰表单。
到目前为止,全选功能将选择并取消选择所有框,当所有框均被选中时,全选框将自动选择。问题在于,当全部选中后,您取消选择了一天中的一个复选框,然后取消选择了全选(这是所希望的),但因为它会触发装饰器中的全部字段,并取消选择所有复选框。
这是装饰器所在的我的容器代码:
import React from 'react'
import { connect } from 'react-redux'
import { Link } from '../../routes'
import { selectAvailability, selectUser, selectWorkExperience, updateUser } from '../../modules/users'
import WorkerUpdateForm from '../../components/WorkerUpdateForm'
import { mapFinalFormErrors } from '../../lib/utils'
import { Form } from 'react-final-form'
import { map, mapValues, merge, size } from 'lodash'
import { retrieveShiftTypes, selectShiftTypes } from '../../modules/shifts'
import { getStripeConnectUrl } from '../../modules/workers'
import createDecorator from 'final-form-calculate'
const defaultAvailability = {
all: false,
monday: false,
tuesday: false,
wednesday: false,
thursday: false,
friday: false,
saturday: false,
sunday: false,
}
const mapErrors = mapFinalFormErrors('Failed to update account')
const mapDispatchToProps = { updateUser, retrieveShiftTypes, getStripeConnectUrl }
const mapStateToProps = (state) => ({
user: selectUser(state),
workExperience: selectWorkExperience(state),
shiftTypes: map(selectShiftTypes(state)),
availability: selectAvailability(state),
})
const decorator = createDecorator(
{
field: 'all',
isEqual: (lastValue, nextValue) => lastValue === nextValue,
updates: {
'day[0]': (value) => value,
'day[1]': (value) => value,
'day[2]': (value) => value,
'day[3]': (value) => value,
'day[4]': (value) => value,
'day[5]': (value) => value,
'day[6]': (value) => value,
}
},
{
field: /day\[\d\]/,
updates: {
all: (firstValue, allValues) => (allValues.day || true)
.reduce((lastValue, nextValue) => lastValue && nextValue)
}
}
)
class WorkerDashboardContainer extends React.Component {
state = {
availability: {
monday: false,
tuesday: false,
wednesday: false,
thursday: false,
friday: false,
saturday: false,
sunday: false,
},
pending: false,
}
componentDidMount() {
const { retrieveShiftTypes, availability } = this.props
if (availability !== null) {
this.setState({ availability: availability })
}
retrieveShiftTypes()
}
openStripeConnect = (event) => {
event.preventDefault()
const { getStripeConnectUrl, user } = this.props
this.setState({ pending: true })
getStripeConnectUrl(user.profileable.id)
.then(response => {
window.location.href = response.url
})
.catch(() => (
this.setState({ pending: false })
))
}
onSubmit = (values) => {
const { updateUser, workExperience, user } = this.props
const workerExperiencesAttributes = map(merge(
{},
workExperience,
mapValues(values.workerExperiencesAttributes, (value, key) => ({
numberOfYears: value,
shiftTypeId: key.match(/^\d+/)[0],
}))
))
const updatedUser = {
...values,
profileable_attributes: {
id: user.profileable.id,
zipCode: values.zipCode,
availability: this.state.availability,
workerExperiencesAttributes,
},
}
return updateUser(user.id, updatedUser)
.catch(mapErrors)
}
render() {
const { user, workExperience, shiftTypes, availability } = this.props
let initialValues
if (size(workExperience) > 0) {
initialValues = {
workerExperiencesAttributes: mapValues(workExperience, 'numberOfYears')
}
} else {
initialValues = {
workerExperiencesAttributes: shiftTypes.reduce((accumulator, type) => ({
...accumulator,
[`${type.id}_${type.name}`]: 0,
}), {})
}
}
let initialAvailability = availability ? availability : defaultAvailability
initialValues = {
...initialValues,
firstName: user.firstName,
lastName: user.lastName,
zipCode: user.profileable.zipCode,
phoneNumber: user.phoneNumber,
email: user.email,
availability: initialAvailability,
}
return (
<Form
component={WorkerUpdateForm}
onSubmit={this.onSubmit}
handleLogout={this.props.handleLogout}
openStripeConnect={this.openStripeConnect}
initialValues={initialValues}
shiftTypes={shiftTypes}
stripeIsSetup={user.profileable.stripeIsSetup}
decorators={[decorator]}
/>
)
}
}
WorkerDashboardContainer = connect(mapStateToProps, mapDispatchToProps)(WorkerDashboardContainer)
export default WorkerDashboardContainer
这是表格:
import React from 'react'
import { Button, FormGroup, HelpBlock, Image, Panel } from 'react-bootstrap'
import { Field } from 'react-final-form'
import styles from '../styles/rc-slider.css.js'
import FieldGroup from './FieldGroup'
import { email, required } from '../lib/validators'
import CheckboxAdapter from './CheckboxAdapter'
import { SliderAdapter } from './worker-registration/WorkExperienceForm'
const WorkerUpdateForm = ({handleSubmit, openStripeConnect, handleLogout, submitFailed, submitError, submitting, availability, shiftTypes, stripeIsSetup}) => (
<Panel>
<form onSubmit={handleSubmit}>
<h4>Payment Information</h4>
{stripeIsSetup ? (
<div>
<p>View your shift payouts and update your payment information on your Stripe Dashboard.</p>
<Button
block
bsSize="lg"
bsStyle="info"
onClick={openStripeConnect}
>View Stripe Dashboard</Button>
</div>
) : (
<div>
<p>Qwick uses Stripe to get you paid quickly and keep your personal and payment information secure. Thousands of companies around the world trust Stripe to process payments for their users.</p>
<Button
block
bsSize="lg"
bsStyle="info"
onClick={openStripeConnect}
>Setup Stripe Now</Button>
</div>
)}
<Image
alt="Powered by Stripe"
className="text-center"
src="/static/images/powered_by_stripe.svg"
responsive
/>
<br />
<h4>Your Information</h4>
<Field
id="firstName"
name="firstName"
label="First Name*"
type="text"
component={FieldGroup}
validate={required}
/>
<Field
id="lastName"
name="lastName"
label="Last Name*"
type="text"
component={FieldGroup}
validate={required}
/>
<Field
id="zipCode"
name="zipCode"
label="Zip Code*"
type="number"
component={FieldGroup}
validate={required}
/>
<Field
id="mobileNumber"
name="phoneNumber"
label="Mobile Number*"
type="tel"
component={FieldGroup}
validate={required}
/>
<Field
id="email"
name="email"
label="Email*"
type="email"
component={FieldGroup}
validate={value => required(value) || email(value)}
/>
<br />
<h4>Work Experience</h4>
{shiftTypes.map((type) => (
<Field
key={type.id}
name={`workerExperiencesAttributes.${type.id}_${type.name}`}
label={type.name}
component={SliderAdapter}
/>
))}
<br />
<h4>Availability</h4>
<Field
id="all"
name="all"
label="Select All"
component={CheckboxAdapter}
/>
<Field
id="monday"
name="day[0]"
label="Monday"
component={CheckboxAdapter}
/>
<Field
id="tuesday"
name="day[1]"
label="Tuesday"
component={CheckboxAdapter}
/>
<Field
id="wednesday"
name="day[2]"
label="Wednesday"
component={CheckboxAdapter}
/>
<Field
id="thursday"
name="day[3]"
label="Thursday"
component={CheckboxAdapter}
/>
<Field
id="friday"
name="day[4]"
label="Friday"
component={CheckboxAdapter}
/>
<Field
id="saturday"
name="day[5]"
label="Saturday"
component={CheckboxAdapter}
/>
<Field
id="sunday"
name="day[6]"
label="Sunday"
component={CheckboxAdapter}
/>
{submitFailed &&
<FormGroup validationState="error">
<HelpBlock>{submitError}</HelpBlock>
</FormGroup>
}
<Button
block
bsSize="lg"
bsStyle="link"
className="logout"
onClick={handleLogout}
>Log Out</Button>
<Button
block
bsSize="lg"
bsStyle="primary"
disabled={submitting}
type="submit"
>{submitting ? 'Saving...' : 'Save'}</Button>
<style jsx global>{styles}</style>
<style jsx>{`
form :global(.checkbox){
border-bottom: 1px solid #eee;
padding: 0 0 20px 0;
margin: 0 0 20px 0;
display:block;
}
h4{
margin: 20px 0 20px 0;
width: 100%;
}
:global(main.main-wrap){
padding-bottom: 120px;
}
:global(main.main-wrap .component-centered .btn-primary){
bottom: 60px;
}
:global(.btn.logout){
position: absolute;
left: 0;
bottom: 5px;
margin: 0;
letter-spacing: 0;
}
:global(.btn.logout:hover){
text-decoration: none;
}
:global(img.text-center){
margin-top: 15px;
}
`}</style>
</form>
</Panel>
)
export default WorkerUpdateForm