David Panart David Panart - 3 months ago 42
React JSX Question

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 !

Answer

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. Another one is to pass a callback function to MailComposer which can be used to update the value. It is possible to develop more, but it really depends your preference for your very application.

Comments