1

I'm looking to create a functional component that takes in a hook and its Set function as parameters and constructs an associated input using those fields (along with some other fields). Running into issues with passing in spread operators (as advised in "Learning React, 2nd edition"). Relevant snippets below, help appreciated!

  1. Generic function that takes in an initial value and returns a new hook and its setValue function:
    export const useInput = (initialValue) => {
      const [value, setValue] = useState(initialValue);
      return [
        { value, onChange: (e) => setValue(e.target.value) },
        () => setValue(initialValue),
      ];
    };
  1. The hook to be used:
    const [firstName, setFirstName] = useInput("");
  1. The functional component (after some cleanup)
      const MyInputComp = (props) => {
        return (
          <div>
            <input
              className="blah-blah-blah"
              {props.obj}   // *ERROR*
              type="text"
              placeholder={props.placeholder}
              required={props.required}
            />
          </div>
        );
      };
  1. Here's how the component is invoked:
    <MyInputComp
        text="First Name"
        obj={...firstName}  // *ERROR*
        placeholder="Jon"
        required={true}
    />

2 Answers 2

1

Issue

You are not consuming the input value and updater correctly.

The are returned as { value, onChange: (e) => setValue(e.target.value) }

but you are passing singly as obj

obj={...firstName}

and

const MyInputComp = (props) => {
  return (
    <div>
      <input
        className="blah-blah-blah"
        {props.obj}   // *ERROR*
        type="text"
        placeholder={props.placeholder}
        required={props.required}
      />
    </div>
  );
};

Solution

Spread the value and onChange props into the underlying input

const MyInputComp = (props) => {
  return (
    <div>
      <input
        className="blah-blah-blah"
        {...props.obj} // <-- spreads value & onChange props
        type="text"
        placeholder={props.placeholder}
        required={props.required}
      />
    </div>
  );
};

Since MyInputComp appears to be simply proxying some props you can spread them all in and then destructure what you need.

const MyInputComp = (props) => {
  return (
    <div>
      <input
        className="blah-blah-blah"
        {...props} // <-- spread all passed props
        type="text"
      />
    </div>
  );
};

Use

const [firstName, setFirstName] = useInput("");

<MyInputComp
    text="First Name"
    {...firstName} // <-- spread to input
    placeholder="Jon" // <-- spread to input
    required={true} // <-- spread to input
/>
Sign up to request clarification or add additional context in comments.

1 Comment

Excellent! Thanks not just for the solution, but for the guidance on how to improve it further.
1

You will need to do couple of changes. First spread the props.obj object so that input element under the hood can take value and onChange props. Such as

const MyInputComp = (props) => {
  return (
    <div>
      <input
        className="blah-blah-blah"
        {...props.obj}
        type="text"
        placeholder={props.placeholder}
        required={props.required}
      />
    </div>
  );
};

Then render the MyInputComp like that

 <MyInputComp
      text="First Name"
      obj={firstName}
      placeholder="Jon"
      required={true}
      onChange={handleNameChange}
/>

Also in the custom useInput hook the change function should be like

(value) => setValue(value)

instead of () => setValue(initialValue)

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.