--- Handling a change to Redux-Form data from container to component via props | Rachel Cantor
Skip to content

Handling a change to Redux-Form data from container to component via props

Published:

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;

Previous Post
The Importance of Independent Volume Control
Next Post
Tinkering with CartoDB to make data visualizations