2
\$\begingroup\$

HOW IT WORKS - When you update a field, all of the fields after it are cleared. (It must work this way to ensure the user fills it out in order. Also a field depends on the value in the previous field) Each child component has a useEffect which should reset the field if the parent tells it to do so, then tell the parent that it has been reset by setting setClear**** back to false;

I've created a simplified example below, that resembles the structure of a more complicated form I have created in another application.

Although it works, I'm unhappy with how fields are being cleared. I feel that it should be more simple. If my form grows by adding just a single element, for example 'Name', I'd have to add state 'Name', add state for 'clearName', and add another case to my switch statement. To me, I think this is too complex considering my actual application has 8 input fields.

(The child components in the actual application are not as simple and repetitive as they are in this example... this has simplified just to focus on the clearing aspect of the code)

Any suggested improvements for my design structure would be much appreciated!

PARENT COMPONENT

import React, { useState, useEffect } from 'react';

const ParentComponent = () => {
  const SPECIES_FIELD = 'speciesField';
  const BREED_FIELD = 'breedField';
  const COLOR_FIELD = 'colorField';
  const ALL = 'all';

  const [species, setSpecies] = useState('');
  const [breed, setBreed] = useState('');
  const [color, setColor] = useState('');

  const [clearSpecies, setClearSpecies] = useState('');
  const [clearBreed, setClearBreed] = useState('');
  const [clearColor, setClearColor] = useState('');

  const resetElementsAfter = field => {
    switch (field) {
      case ALL:
        setClearSpecies(true);
        setClearBreed(true);
        setClearColor(true);
      case SPECIES_FIELD:
        setClearBreed(true);
        setClearColor(true);
        break;
      case BREED_FIELD:
        setClearColor(true);
        break;
    }
  };

  return (
    <>
      <ChooseSpecies changed={val => {resetElementsAfter(SPECIES_FIELD); setSpecies(val);}} clearField={clearSpecies} setClearField={setClearSpecies}/>
      <ChooseBreed changed={val => {resetElementsAfter(BREED_FIELD); setBreed(val);}} clearField={clearBreed} setClearField={setClearBreed}/>
      <ChooseColor changed={setColor} clearField={clearColor} setClearField={setClearColor}/>

      <div>Species: {species}</div>
      <div>Breed: {breed}</div>
      <div>Color: {color}</div>

      <Button onClick={() => resetElementsAfter(ALL)} />
    </>
  );
}

CHILD COMP - 1ST FIELD

import React, { useState, useEffect } from 'react';

const ChooseSpecies = ({ changed, clearField, setClearField }) => {
  const values = ['Dog', 'Cat'];

  const [fieldValue, setFieldValue] = ('');

  useEffect(() => {
    if(clearField){
      changed('');
      setFieldValue('');
      setClearField(false);
    }
  }, [clearField]);

  const handleChange = e => {
    setFieldValue(e.target.value);
    changed(e.target.value);
  };

  <Dropdown values={values} fieldValue={fieldValue} changed={handleChange}/>
}

CHILD COMP - 2ND FIELD

import React, { useState, useEffect } from 'react';

const ChooseBreed = ({ changed, clearField, setClearField }) => {
  const values = [{
    Dog: ['Lab', 'Staffie'],
    Cat: ['Maine Coone, Sphynx']
  }];

  const [fieldValue, setFieldValue] = ('');

  useEffect(() => {
    if(clearField){
      changed('');
      setFieldValue('');
      setClearField(false);
    }
  }, [clearField]);

  const handleChange = e => {
    setFieldValue(e.target.value);
    changed(e.target.value);
  };

  <Dropdown values={values} fieldValue={fieldValue} changed={handleChange}/>
}

CHILD COMP - 3RD FIELD

import React, { useState, useEffect } from 'react';

const ChooseColor = ({ changed, clearField, setClearField }) => {
  const values = ['Black', 'White', 'Brown'];

  const [fieldValue, setFieldValue] = ('');

  useEffect(() => {
    if(clearField){
      changed('');
      setFieldValue('');
      setClearField(false);
    }
  }, [clearField]);

  const handleChange = e => {
    setFieldValue(e.target.value);
    changed(e.target.value);
  };

  <Dropdown values={values} fieldValue={fieldValue} changed={handleChange}/>
}
\$\endgroup\$

1 Answer 1

4
\$\begingroup\$

If my form grows .. I'd have to add state ... and add state ... and [add] another case to my switch statement

This is a code smell, and not unique to Javascript. This is design problem and the fix has to be at the core of the design - a JS data structure will fix it.

An array of field objects (in the proper order) will eliminate code proliferation. You will new field() for each actual UI field. Pass constructor parameters for field-specific values.

I'm unhappy with how fields are being cleared

"change", "set", "clear" are all the same thing. A field cleared in the course of validating - setting state - is the same as setting any value. It is trivial to iterate the collection and set child fields with no regard to field names. NO switch STATEMENT REQUIRED.

Any suggested improvements for my design structure would be much appreciated!

  • Creating data structures significantly cleans up and simplifies code. The data structure is the UI state

  • A collection (e.g. array) of same-type objects, scales very well. The more dependencies among UI entries, the more bang for the buck. Hear me now and believe me later!

  • How it works

    • Populate the data structure from the UI. Include all UI entry values and properties needed for setting visual state, like graying out or hiding.

    • Validate data structure setting its object properties to a valid state.

    • Replace all UI fields/properites from the data structure.

    • Pass all values/properties every time. Never this: "field x was not changed so don't pass it" - irrelevant because the data structure is always evaluated top to bottom and always ends up in a valid state.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.