Search code examples
javascriptreactjsstyled-components

Uncontrolled child component with styled-components


I'm trying to create a reusable component that is an uncontrolled input. Normally this works fine, but I cannot get it to work with styled-components applied to the child component (value returns undefined).

class Parent extends Component {
  handleSubmit = (event) => {
    console.log(this.firstName.value)
    event.preventDefault();
  }
  render() {
    return (
      <form onSubmit={this.handleSubmit}>
          <label>First Name</label>
          <UncontrolledInput
            defaultValue={'fish'}
            inputType='text'
            name='firstName'
            inputRef={(element) => {this.firstName = element}}
          />
          <button type="submit">
            Submit
          </button>
      </form>
    )
  }
}


const StyledInput = styled.input`
  border: 3px solid;
`;

const UncontrolledInput = (props) => {
  const {name, inputType, defaultValue, inputRef} = props;
    return (
    <StyledInput
      name={name}
      type={inputType}
      defaultValue={defaultValue ? defaultValue : ''}
      ref={inputRef}
    />
  )
};

Solution

  • styled-components wraps elements into a react component. Passing a ref prop to it will not give you the reference to the DOM element but to the wrapper component.

    In the styled-components docs is described how to obtain a ref to the underlying DOM element:

    Passing a ref prop to a styled component will give you an instance of the StyledComponent wrapper, but not to the underlying DOM node. This is due to how refs work. It's not possible to call DOM methods, like focus, on our wrappers directly.

    To get a ref to the actual, wrapped DOM node, pass the callback to the innerRef prop instead.

    So just change ref to innerRef on your <StyledInput>component:

    const UncontrolledInput = (props) => {
      const {name, inputType, defaultValue, inputRef} = props;
        return (
        <StyledInput
          name={name}
          type={inputType}
          defaultValue={defaultValue ? defaultValue : ''}
          innerRef={inputRef}
        />
      )
    };
    

    Here is a working example.