skip to Main Content

Can anyone point help me to find out why onChange is never being executed?

Basically I’m extending ChakraUI’s Input component to load a value from localStorage if such value exists.

The component is correctly loading the localStorage value, but if I try to set the value property from the value state, the component becomes read-only, which means it’s an uncontrolled component & etc, but defaultValue is supposed to circumvent this. What is the matter here?

I even tried a hack using an useEffect hook to read localStorage and then directly update the input using classic JavaScript DOM manipulation. It correctly updates the value but still doesn’t trigger onChange.

I also tried using onBlur instead to avoid any potential problems from re-renders, which wasn’t being triggered as well.

What am I doing wrong?

import { ChangeEvent, useState, forwardRef } from 'react';
import { Input, InputProps } from '@chakra-ui/react';

interface StoredInputProps {
  name: string;
  defaultValue?: string;
  placeholder?: string ;
  autoCapitalize?: string;
}

type CombinedInputProps = StoredInputProps & InputProps;

const StoredInput= forwardRef<HTMLInputElement, CombinedInputProps>(
  ({ name, defaultValue = '', placeholder = '', autoCapitalize = 'none', ...props }, ref) => {
  const [value, setValue] = useState<string>(() => {
    const storedValue = localStorage.getItem(name);
    return storedValue !== null ? storedValue : defaultValue;
  });

  const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
    console.log("Handle Change")
    const newValue = event.target.value;
    // Immediately update localStorage when the value changes
    localStorage.setItem(name, newValue);
  };

  return (
    <>
      <Input
        id={name}
        name={name}
        defaultValue={value}
        onChange={(event) => handleChange(event)}
        placeholder={placeholder}
        autoCapitalize={autoCapitalize}
        {...props}
      />
    </>
  );
});

export default StoredInput;

2

Answers


  1. Chosen as BEST ANSWER

    The {...props} destructuring at the end of the component was overwriting my own value for onChange. Moving it to the top declaration solved the issue.


  2. The useState doesn’t change each time the storage value change.
    A better idea would be to set the value of directly after you set the storage value.
    This is how you would implement it:

    import { ChangeEvent, useState, forwardRef } from 'react';
    import { Input, InputProps } from '@chakra-ui/react';
    
    interface StoredInputProps {
      name: string;
      defaultValue?: string;
      placeholder?: string ;
      autoCapitalize?: string;
    }
    
    type CombinedInputProps = StoredInputProps & InputProps;
    
    const StoredInput= forwardRef<HTMLInputElement, CombinedInputProps>(
      ({ name, defaultValue = '', placeholder = '', autoCapitalize = 'none', ...props }, ref) => {
      const [value, setValue] = useState<string>(() => {
        const storedValue = localStorage.getItem(name);
        return storedValue !== null ? storedValue : defaultValue;
      });
    
      const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
        console.log("Handle Change")
        const newValue = event.target.value;
        // Immediately update localStorage when the value changes
        localStorage.setItem(name, newValue);
        //Setting the use state
        setValue(newValue)
      };
    
      return (
        <>
          <Input
            id={name}
            name={name}
            defaultValue={value}
            onChange={(event) => handleChange(event)}
            placeholder={placeholder}
            autoCapitalize={autoCapitalize}
            {...props}
          />
        </>
      );
    });
    
    export default StoredInput;
    

    Also you can use a useEffect but it would be a wast of time.
    I hope this answer will help you

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search