reactjsreact-hookscontrolled-component

React - Uncaught TypeError: Cannot read property 'toLowerCase' of undefined


After entering the color name in the input field, when I submit the form, an error occurs :

TypeError: Cannot read property 'toLowerCase' of undefined (anonymous function) C:/Users/HP/Documents/WDB/React/Practice/colors-app/src/NewPaletteForm.js:117

  114 |     //to check -> is 'palette name' unique
  115 |     ValidatorForm.addValidationRule("isPaletteNameUnique", value => {
  116 |         return palettes.every(
> 117 |             ({ paletteName }) => paletteName.toLowerCase() !== value.toLowerCase()
  118 | ^       );
  119 |     });
  120 | })

App.js : (Class-based component)

class App extends Component {
  constructor(props) {
    super(props);
    this.state = { palettes: seedColors };
    this.findPalette = this.findPalette.bind(this);
    this.savePalette = this.savePalette.bind(this);
  }
savePalette(newPalette) {
    this.setState({ palettes: [...this.state.palettes, newPalette] });
  }

  render() {
    return (
      <Switch>
        <Route
          exact
          path='/palette/new'
          render={(routeProps) =>
            <NewPaletteForm
              savePalette={this.savePalette}
              palettes={this.state.palettes}
              {...routeProps}
            />}
        />

NewPaletteForm.js : (Functional component and uses react hooks)

function NewPaletteForm(props) {
    const classes = useStyles();
    const [open, setOpen] = useState(false);
    const [currentColor, setCurrentColor] = useState('teal');
    const [colors, setColors] = useState([{ color: 'pink', name: 'pink' }]);
    const [fields, setFields] = useState({
        newColorName: '',
        newPaletteName: ''
    })

    useEffect(() => {
        ValidatorForm.addValidationRule('isColorNameUnique', (value) => { 
            return colors.every(
                ({ name }) => name.toLowerCase() !== value.toLowerCase()
            );
        });

        ValidatorForm.addValidationRule('isColorUnique', (value) => { 
            return colors.every(
                ({ color }) => color !== currentColor
            );
        });

        ValidatorForm.addValidationRule("isPaletteNameUnique", value => {
            return props.palettes.every(
                ({ paletteName }) => paletteName.toLowerCase() !== value.toLowerCase()
            );
        });
    })
  function addNewColor() {
    const newColor = {
        color: currentColor,
        name: fields.newColorName
    }
    setColors(oldColors => [...oldColors, newColor]);
    setFields({ newColorName: '' });
};

function handleChange(evt) {
    setFields({ ...fields, [evt.target.name]: evt.target.value });

}

function handleSubmit() {
    let newName = fields.newPaletteName;
    const newPalette = {
        paletteName: newName,
        id: newName.toLowerCase().replace(/ /g, '-'),
        colors: colors
    }
    props.savePalette(newPalette);
    props.history.push('/');
}

Validator form components for colors and palettes :

<ValidatorForm onSubmit={handleSubmit}>
                        <TextValidator
                            label='Palette Name'
                            value={fields.newPaletteName}
                            name='newPaletteName'
                            onChange={handleChange}
                            validators={['required', 'isPaletteNameUnique']}
                            errorMessages={['Enter Palette Name', 'Name already used']} />
                        <Button variant='contained' color='primary' type='submit'>
                            Save Palette
                        </Button>
                    </ValidatorForm>

<ValidatorForm onSubmit={addNewColor}>
                    <TextValidator
                        value={fields.newColorName}
                        name='newColorName'
                        onChange={handleChange}
                        validators={['required', 'isColorNameUnique', 'isColorUnique']}
                        errorMessages={['Enter a color name', 'Color name must be unique', 'Color already used!']}
                    />
                    <Button
                        variant='contained'
                        type='submit'
                        color='primary'
                        style={{
                            backgroundColor: currentColor
                        }}
                    >
                        Add Color
                    </Button>
                </ValidatorForm>

seedColors.js:

export default [
    {
        paletteName: "Material UI Colors",
        id: "material-ui-colors",
        emoji: "🎨",
        colors: [
            { name: "red", color: "#F44336" },
            { name: "pink", color: "#E91E63" },
            { name: "purple", color: "#9C27B0" },
            { name: "deeppurple", color: "#673AB7" },
            { name: "indigo", color: "#3F51B5" },
            { name: "blue", color: "#2196F3" },
            { name: "lightblue", color: "#03A9F4" },
            { name: "cyan", color: "#00BCD4" },
            { name: "teal", color: "#009688" },
            { name: "green", color: "#4CAF50" },
            { name: "lightgreen", color: "#8BC34A" },
            { name: "lime", color: "#CDDC39" },
            { name: "yellow", color: "#FFEB3B" },
            { name: "amber", color: "#FFC107" },
            { name: "orange", color: "#FF9800" },
            { name: "deeporange", color: "#FF5722" },
            { name: "brown", color: "#795548" },
            { name: "grey", color: "#9E9E9E" },
            { name: "bluegrey", color: "#607D8B" }
        ]
    },
    {
        paletteName: "Flat UI Colors v1",
        id: "flat-ui-colors-v1",
        emoji: "🤙",
        colors: [
            { name: "Turquoise", color: "#1abc9c" },
            { name: "Emerald", color: "#2ecc71" },
            { name: "PeterRiver", color: "#3498db" },
            { name: "Amethyst", color: "#9b59b6" },
            { name: "WetAsphalt", color: "#34495e" },
            { name: "GreenSea", color: "#16a085" },
            { name: "Nephritis", color: "#27ae60" },
            { name: "BelizeHole", color: "#2980b9" },
            { name: "Wisteria", color: "#8e44ad" },
            { name: "MidnightBlue", color: "#2c3e50" },
            { name: "SunFlower", color: "#f1c40f" },
            { name: "Carrot", color: "#e67e22" },
            { name: "Alizarin", color: "#e74c3c" },
            { name: "Clouds", color: "#ecf0f1" },
            { name: "Concrete", color: "#95a5a6" },
            { name: "Orange", color: "#f39c12" },
            { name: "Pumpkin", color: "#d35400" },
            { name: "Pomegranate", color: "#c0392b" },
            { name: "Silver", color: "#bdc3c7" },
            { name: "Asbestos", color: "#7f8c8d" }
        ]
    }
]

Solution

  • What you can do is check to see if the value exists before calling toLowerCase. Try using ?., like this

    Instead of using value.toLowerCase() use value?.toLowerCase().

    That way if the value is undefined or null, it won't call toLowerCase()

    If paletteName is the one failing you can use paletteName?.toLowerCase()

    If you want to go completely safe you do

    paletteName?.toLowerCase() !== value?.toLowerCase()