简体   繁体   中英

ReactiveVar not re-rendering React component

I'm quite new to React in Meteor.

TL;DR : In my data container, changing the value of my ReactiveVar do not rerender my view.

I've got this code :

import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import { Meteor } from 'meteor/meteor';
import { createContainer } from 'meteor/react-meteor-data';

// Mail composer - interface for generating enews
class MailComposer extends Component {
  constructor(props) {
    super(props);
  }

  handleSubmit( event ) {
    event.preventDefault();

    const text = this.refs.mailText.value.trim(),
          title = this.refs.mailTitle.value.trim(),
          rcpts = this.refs.mailRecpts.value.trim();

    console.log(text, title, rcpts);

    if ( text && title && rcpts ) {
      // ...
      this.props.hasTestedIt.set( true );
    } else {
      // ...
    }
  }

  componentDidMount () {
    console.log(this);
    $( this.refs.textArea ).autosize();
  }

  getBtnText () {
    return ( this.props.hasTestedIt.get() ? "Send it" : "Test it" );
  }

  render() {
    let self = this;
    Tracker.autorun(function(){
      console.log(self.props.hasTestedIt.get());
    });

    return (
      <div className="panel panel-default panel-primary">
        <div className="panel-heading">
          <h3 className="panel-title">Mail composer</h3>
        </div>
        <div className="panel-body">
          <form className="form-group" onSubmit={this.handleSubmit.bind(this)}>
            <div className="input-group">
              <span className="input-group-addon" id="basic-addon1">Title</span>
              <input type="text" className="form-control" ref="mailTitle" />
            </div>
            <br/>
            <label htmlFor="js-recipients">Recipients:</label>
            <select className="form-control" width="width: initial;" id="js-recipients" ref="mailRecpts">
              <option>Admins</option>
              <option>All</option>
            </select>
            <br/>
            <label htmlFor="comment">Text:</label>
            <textarea className="form-control" rows="5" ref="mailText"></textarea>
            <br/>
            <button className="btn btn-primary" type="submit">
              {this.getBtnText()}
            </button>
          </form>
        </div>
      </div>
    );
  }
}

MailComposer.propTypes = {
  hasTestedIt: PropTypes.object.isRequired
};

export default createContainer( () => {
  return {
    hasTestedIt: new ReactiveVar( false )
  };
}, MailComposer);`

But when I set my ReactiveVar prop in my submit handler, the text returned by the getBtnText method in the button do not change. I've tried to put the ternary directly inside the HTML, but I got the same result. The var is correctly setted to true, as the autorun correctly log me the change.

In another component, from which this one has been copied, I do correctly rerender the component, but using a .fetch() on a find to map the returned array in a renderTasks method which render a list of new components.

What am I missing please ? And how could I fix it ?

Thanks a lot !

The component doesn't update because the passed hasTestedIt prop is not changed itself. It's the value it holds that changed.

Watch the value you are actually interested in instead.

// Declare the reactive source somewhere appropriately.
const hasTestedIt = new ReactiveVar( false );

// Watch its value instead.
export default createContainer( () => {
  return {
    hasTestedIt: hasTestedIt.get()
  };
}, MailComposer);`

Note that it is now the value passed to MailComposer , not a ReactiveVar that can be referenced to update the value anymore. How do we update it in this case?

One simplest approach is to pass the hasTestedIt as well as before, though this wouldn't be my personal recommendation.

// Watch its value instead.
export default createContainer( () => {
  return {
    // Reactive source that triggers re-rendering.
    valueOfHasTestedIt: hasTestedIt.get(),
    // Provided for the contained component to reference to set new values.
    // Leaving this alone doesn't trigger re-rendering!
    hasTested
  };
}, MailComposer);

It's not elegant IMO. Another one is to pass a callback function to MailComposer which can be used to update the value.

const updateHasTestedIt = ( value ) => hasTestedIt.set( value );

// Watch its value instead.
export default createContainer( () => {
  return {
    hasTestedIt: hasTestedIt.get(),
    updateHasTestedIt,
  };
}, MailComposer);

class MailComposer extends Component {
  ...
  handleSubmit( event ) {
    ...
    this.props.updateHasTestedIt( true );
    ...
  }
  ...
};

It's better this time.

It is possible to develop more, but it really depends on your preference for your very application. You and only you can make the design decision.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM