mustang
mustang

Reputation: 161

Semantic UI forms with React

I'm building a simple login form with Semantic UI in conjunction with React in ES6 JS. The code below functions as expected. I would like to know if I'm doing things in the right way and following React convention:

constructor(props) {
    super(props);

    // this is getInitialState()
    this.state = {
      emailaddress: '',
      password: '',
      errors: ''
    };
}

componentDidMount() {
    $('.ui.form').form({
        fields: {
            email: {
                identifier  : 'emailaddress',
                rules: [
                {
                    type   : 'empty',
                    prompt : 'Please enter your e-mail'
                },
                {
                    type   : 'email',
                    prompt : 'Please enter a valid e-mail'
                }
                ]
            },
            password: {
                identifier  : 'password',
                rules: [
                {
                    type   : 'empty',
                    prompt : 'Please enter your password'
                },
                {
                    type   : 'length[6]',
                    prompt : 'Your password must be at least 6 characters'
                }
                ]
            }
        },
        inline: true,
        onFailure: this.handleInvalidForm.bind(this),
        onSuccess: this.handleValidForm.bind(this)
    });
}

login() {
    // Make call to auth service which will redirect on success or throw an error here if it fails
}

handleInvalidForm(e) {
    this.setState({errors : e});
    return false;
}

handleValidForm(e) {
    e.preventDefault();
    this.login();
}

render() { 
    return (
        <form className="ui large form" >
            <input type="text" valueLink={this.linkState('emailaddress')} name="emailaddress" placeholder="E-mail address"/>
            <input type="password" valueLink={this.linkState('password')} name="password" placeholder="Password"/>
            <div className="ui fluid big green submit button">Login</div>
        </form>
)

ReactMixin(Login.prototype, React.addons.LinkedStateMixin);

Currently validation errors are set to occur inline. This means the plugin injects code into the DOM. This is not part of my state dictionary in React. Is this a problem?

Upvotes: 3

Views: 7628

Answers (2)

Dan Prince
Dan Prince

Reputation: 29989

I guess the React convention would be to not use a library like Semantic UI, which uses jQuery to handle component state and updating.

However, sometimes you have to integrate with third party libraries, so we'll assume that the above is not an option.

Third Party DOM

It doesn't look like the validation API for semantic UI forms will let you intercept the errors — they just go straight into the DOM. When that's the case, it's more trouble than its worth to do anything about it.

Using Refs

The next thing to look at is the way you get a reference to .ui.form. It's possible to use the ref property with a callback to get hold of an element in a more conventional way.

addValidation(element) {
  const $element = $(element);
  $element.form({
    // ...
  });
},
render() { 
  return (
    <form className="ui large form" ref={this.addValidation}>
      <input type="text" valueLink={this.linkState('emailaddress')} name="emailaddress" placeholder="E-mail address"/>
      <input type="password" valueLink={this.linkState('password')} name="password" placeholder="Password"/>
      <div className="ui fluid big green submit button">Login</div>
    </form>
  )
)

Rather than introducing your validation using a jQuery selector and the componentDidMount lifecycle hook, you can simply create a handler for the element, which will be called when there is a reference available.

Consider Migrating from LinkedStateMixin

The official word on LinkedState:

If you're new to the framework, note that ReactLink is not needed for most applications and should be used cautiously.

Combine this with the fact that mixins introduce ambiguity and may be deprecated in the future.

changeEmail(event) {
  const { value: emailaddress } = event.target;
  this.setState({ emailaddress });
}
changePassword(event) {
  const { value: password } = event.target;
  this.setState({ password });
}
render() { 
  return (
    <form className="ui large form" ref={addValidation}>
      <input type="text" onChange={changeEmail} name="emailaddress" placeholder="E-mail address"/>
      <input type="password" onChange={changePassword} name="password" placeholder="Password"/>
      <div className="ui fluid big green submit button">Login</div>
    </form>
  )
)

This approach doesn't introduce much new code, it removes your dependency on LinkedStateMixin and it gives you more flexibility when it comes to handling the change events yourself.

Here's an interesting article on how inheritance can be used to solve some of the issues regarding mixins. Particularly important is the discussion at the end about inheriting from PureComponent rather than using the PureRenderMixin. This might be a way to remove all mixin dependencies from your code.

Use Static Properties

Assuming you aren't going to dynamically generate the fields in your form, you can pull the validation rules out of your lifecycle hook and move them into a static variable, shared between all instances of your component.

class YourComponent extends React.Component {
  // ...
  addValidation(element) {
    const $element = $(element);
    $element.form({
      // ...
      fields: YourComponent.validationFields,
      inline: true,
      onFailure: this.handleInvalidForm.bind(this),
      onSuccess: this.handleValidForm.bind(this)
    });
  },
}

YourComponent.validationFields = {
  email: {
    // ...
  },
  password: {
    // ...
  }
}

There's no need to define it once for each instance of the component. You could even pull it out into its own module or JSON file if you wanted.

Conclusion

On the whole, these aren't drastic changes. If you use a third party library that already has opinions about how to interact with the DOM, then it's never going to operate seamlessly with your React code.

There are a handful of React libraries that also solve this problem, just so that you are aware.

Both of these libraries solve the same problem in a more idiomatic way and there are examples provided for both. However, they won't give you the styles and layout that Semantic UI provides.

Perhaps a good middleground would be to include Semantic's stylesheets, but not its Javascript libraries. Then you can use the appropriate class names to get the styles, but instead use a React library for validating your forms.

Upvotes: 5

luanped
luanped

Reputation: 3198

You should add a ref to the form you render, and rather than using jQuery to select the form in componentDidMount, use the ref instead. Avoid going directly to the DOM directly if you can.

Upvotes: 0

Related Questions