1

Got tired Firefox's ugly select and not being able to style it. I thought I'd do one in React for learning purposes.

It seems to be easy to implement but I cannot figure out how to do onChange with custom component and how to get the value back with the event. If it is possible at all ...

The Select component looks like this:

type SelectProps = {
  select: {
    value: any
    options: {
      [k: string]: any
    }
  }
}

type SelectState = {
  show: boolean
}

class Select extends Component<SelectProps, SelectState> {
  constructor(props: SelectProps) {
    super(props)
    this.state = {
      show: false
    }
  }
  label = (v: any): string | undefined => {
    for (var k in this.props.select.options) {
      if (this.props.select.options[k] === v) return k
    }
  }
  change = (i: number) => {
    this.setState({ show: false })
    this.props.select.value = this.props.select.options[this.keys[i]]
  }
  display = () => {
    this.setState({ show: !this.state.show })
  }
  keys = Object.keys(this.props.select.options)
  render() {
    let { show } = this.state
    let { options, value } = this.props.select
    return (
      <div className='select'>
        <button onClick={this.display}>{this.label(value)}</button>
        {!show ? null :
          <ul>
            {this.keys.map((e: string, i: number) => (
              <li key={i} onClick={() => this.change(i)}>{e}</li>)
            )}
          </ul>
        }
      </div>
    )
  }
}

It works as expected. I can style it (hooray!).

I get the selected value from value parameter. I wondering though if I can get it with onChange event? So it behaves more like native select.

P.S.

This is styling of it (in stylus), in case it is needed

.select
  display: inline-block
  position: relative
  background: white
  button
    border: .1rem solid black
    min-width: 4rem
    min-height: 1.3rem
  ul
    position: absolute
    top: 100%
    border: .1rem solid black
    border-top: 0
    z-index: 100
    width: 100%
    background: inherit
    li
      text-align: center
      &:hover
        cursor: pointer
        background: grey

Thanks

0

1 Answer 1

1

As part of the props, pass in a change callback. In change, call the callback and pass in the new value:

type SelectProps = {
  select: {
    onChange: any, // change callback
    value: any,
    options: {
      [k: string]: any
    }
  }
}

...
...

change = (i: number) => {
  this.setState({ show: false })
  this.props.select.value = this.props.select.options[this.keys[i]]
  this.props.select.onChange(this.props.select.value); // call it
}

Then you can pass your change callback when outputting:

let s = {
    value: '2',
    options: {
        '1' : 'one',
        '2' : 'two'  
    },
    onChange : function(val){
        console.log("change to " + val);
    }
};

return (
     <Select select={s} />
);
Sign up to request clarification or add additional context in comments.

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.