Search code examples
mongodbmeteorreactjssimple-schemameteor-tracker

How to use node-simple-schema reactively?


Given that there is not much examples about this, I am following the docs as best as I can, but the validation is not reactive.

I declare a schema :

import { Tracker } from 'meteor/tracker';
import SimpleSchema from 'simpl-schema';

export const modelSchema = new SimpleSchema({
  foo: { 
    type: String,
    custom() {
      setTimeout(() => {
        this.addValidationErrors([{ name: 'foo', type: 'notUnique' }]);
      }, 100);  // simulate async
      return false;
    }
  }
}, {
  tracker: Tracker
});

then I use this schema in my component :

export default class InventoryItemForm extends TrackerReact(Component) {

  constructor(props) {
    super(props);

    this.validation = modelSchema.newContext();
    this.state = {
      isValid: this.validation.isValid()
    };
  }

  ...

  render() {
    ...
    const errors = this.validation._validationErrors;

    return (
      ...
    )
  }
}

So, whenever I try to validate foo, the asynchronous' custom function is called, and the proper addValidationErrors function is called, but the component is never re-rendered when this.validation.isValid() is supposed to be false.

What am I missing?


Solution

  • There are actually two errors in your code. Firstly this.addValidationErrors cannot be used asynchronously inside custom validation, as it does not refer to the correct validation context. Secondly, TrackerReact only registers reactive data sources (such as .isValid) inside the render function, so it's not sufficient to only access _validationErrors in it. Thus to get it working you need to use a named validation context, and call isValid in the render function (or some other function called by it) like this:

    in the validation

    custom() {
      setTimeout(() => {
        modelSchema.namedContext().addValidationErrors([
          { name: 'foo', type: 'notUnique' }
        ]);
      }, 100);
    }
    

    the component

    export default class InventoryItemForm extends TrackerReact(Component) {
      constructor(props) {
        super(props);
    
        this.validation = modelSchema.namedContext();
      }
    
      render() {
        let errors = [];
        if (!this.validation.isValid()) {
          errors = this.validation._validationErrors;
        }
    
        return (
          ...
        )
      }
    }
    

    See more about asynchronous validation here.