I ended up realizing that I didn’t need to use context for this after all and used this pattern as an alternative.
import React, { Component } from 'react'
import { connect } from 'react-redux'
import PropTypes from 'prop-types'
import { FieldArray, getFormValues } from 'redux-form' // import the action creator you need
import CountryPicker from '../components/CountryPicker'
class CountriesContainer extends Component {
constructor(props) {
super(props)
this.state = { countries: props.formValues.countries }
}
static propTypes = {
formValues: PropTypes.object
}
componentWillReceiveProps = (nextProps) => {
if (nextProps.formValues && nextProps.formValues.countries !== this.props.formValues.countries) {
this.setState({ countries: nextProps.formValues.countries })
}
}
shouldComponentUpdate = (nextProps) => {
if (nextProps.formValues) return (nextProps.formValues.countries !== this.props.formValues.countries)
}
handleCountryChange = () => this.setState({ countries: this.props.formValues.countries })
render() {
const { countries } = this.state
const options = ['United States', 'Canada', 'Mexico']
return (
<div>
<FieldArray component={CountryPicker}
name='countries'
options={options}
selectedCountries={countries}
onCountryChange={this.handleCountryChange}
/>
</div>
)
}
}
const mapStateToProps = (state) => {
const { countries } = state
return {
countries,
formValues: getFormValues('countriesForm')(state)
}
}
export default connect(
mapStateToProps
)(CountriesContainer)
import React, { Component } from 'react'
import { connect } from 'react-redux'
import PropTypes from 'prop-types'
import { List, Checkbox, Table } from 'semantic-ui-react'
import indexOf from 'lodash/indexOf'
class CountryPicker extends Component {
constructor(props) {
super(props)
this.state = {
options: props.options,
selected: props.selectedCountries,
fields: props.fields
}
this.handleCountryChange = this.handleCountryChange.bind(this)
}
static propTypes = {
options: PropTypes.array.isRequired,
selectedCountries: PropTypes.array.isRequired,
fields: PropTypes.any,
onCountryChange: PropTypes.func.isRequired
}
shouldComponentUpdate(nextProps) {
if (nextProps.selectedCountries !== this.props.selectedCountries) {
this.handleCountryChange()
return true
}
return false
}
componentWillReceiveProps(nextProps) {
this.setState({
selected: nextProps.selectedCountries
})
}
handleCountryChange() {
this.props.onCountryChange(this.state.regionKey)
}
handleSelectAll = () => {
const { options, selected, fields } = this.state
let fieldsToAdd, fieldsToRemove
if (selected.length === options.length) {
// deselect all
fieldsToRemove = selected.map((country) => indexOf(selected, country))
// Making sure the removal of indices won't be affected by race conditions
// by sorting them in descending order
fieldsToRemove.sort((a, b) => b - a)
fieldsToRemove.forEach((countryIndex) => fields.remove(countryIndex))
} else {
fieldsToAdd = options.reduce((result, country) => {
if (indexOf(selected, country) === -1) result.push(country)
return result
}, [])
fieldsToAdd.forEach((country) => fields.push(country))
}
}
handleChange = (e, { value }) => {
const { selected, fields } = this.state
if (indexOf(selected, value) !== -1) {
fields.remove(indexOf(selected, value))
} else {
fields.push(value)
}
}
render() {
const { options, selected } = this.state
return (
<Table compact>
<Table.Header>
<Table.Row>
<Table.HeaderCell>
<Checkbox checked={options.length > 0 && selected.length === options.length} label='Countries'
indeterminate={0 !== selected.length && selected.length < options.length} onChange={this.handleSelectAll} onClick={this.handleSelectAll}
disabled={options.length === 0} />
</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.Cell>
<List verticalAlign='middle' relaxed style={{ height: '180px', overflowY: 'auto' }}>
{options.map((country, i) =>
<List.Item key={i}>
<List.Content>
<Checkbox label={country} name='countries' value={country} checked={indexOf(selected, country) !== -1} onChange={this.handleChange} />
</List.Content>
</List.Item>
)}
</List>
</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
)
}
}
export default CountryPicker