How does Meteor handle React setState?


#1

Hello,

I’m both new to react and to meteor.

I did a tutorial on react that used firebase, then I did the todo tutorial with meteor and it seems to me that when using React with Meteor, the concept of state is a bit different.

For example, in the tutorial, when a task is deleted, it invokes Meteor.call(‘tasks.remove’, this.props.task._id);
This updates the UI. Does Meteor execute a this.setState behind the scenes?


#2

Yes - the Meteor.call sends a request to the server, which returns and updates Meteor’s client-side database, which then gets passed into the React component.

Basically you don’t call setState yourself, the component updates itself when the data changes.


#3

Hey thanks.

I noticed it updates itself, I was wondering if it did so using React’s setState or if it did it in some other way…

Are there any documents on this anywhere?


#4

No, what you probably have done is utilized Meteor’s minimongo package for this, which gives you access to your sever’s mongo databse on the client. You probably have something like this right?

export default createContainer(() => {
  return {
    tasks: Tasks.find({}).fetch(),
  };
}, App);

Here you are creating a wrapper component that passes your query data (tasks) down to the App component as a prop “tasls” in this case. When the task data changes (using Meteor’s Tracker package and Minimongo), the prop changes, and your component re-renders with the new data. So it does not use react’s state functionality, as state is kept in Minimongo, which is synced to your back-end databse that you update with the Meteor.call.

So, This is how it goes:
(client) click event ->
(client) Meteor.call(“tasks.remove”, this.props.task._id) ->
(server) meteor method call is handled ->
(server) updates server side databse ->
(server - client) update is propagated automatically to client mongo database ->
(client) wrapper component re-runs ->
(client) App component receives new data through props ->
(client) App re-renders

Relevant documentation


#5

Hey jorgeer, thanks for the detailed reply

I think I should set something straight: I am trying to understand how Meteor accomplishes the update, not how I should write code to update, which are different things.

It is clear to me that me as a developer should handle state update using the Meteor.call, and that to accomplish that, I should wrap the components using createContainer.

What I am trying to understand is how Meteor interacts with React’s state. Since createContainer updates the props, does it do so using setState? I understood that no, it does not use setState, and I will try to explain why below.

Coming back to the example, I did the tasks tutorial, so I have exactly that function you quoted.

I am particularly interested in this step

(client) App component receives new data through props ->"

I read the documentation you linked and it lead me to https://github.com/meteor/react-packages/tree/devel/packages/react-meteor-data

As I understood it, setState is not used in the process of passing down the props to the wrapped component. I think the component’s state is changed and a forcedUpdate is called

So I guess Meteor’s container wrapper is bypassing the setState somehow, changing the state using Tracker and forcing updates with the new data… Is that it?

Surely I don’t need to understand this to use Meteor, it’s just kind of my learning/thought process to peek under the hood and try to understand what’s going on. I did a “pure” react tutorial before trying meteor and noticed that setState was in the core of React… Since this is so different in Meteor, I was puzzled and I am trying to understand what’s going on. Sorry if I’m being pedantic


#6

Something to think about:

React’s setState is NOT a core-concept of react. It’s a simple way of managing local internal state of a component, but it could also be done with redux or meteor’s createContainer.

You can create a react-app without using setState anywhere.

createContainer is a higher-order-component that returns a new component where some properties come from a reactive meteor datasource.


#7

When you use redux and change the global state, how does redux tell react it needs to recalc the screen? (if not via a setState)


#8

react does also recalc the screen if a component is mounted with different properties. But best look at the implementation details in redux.


#9

Let me rephrase, if setState isn’t a core concept then how can you tell react the state of the app changed? (without setState, force update, or an abstraction of them)

Changing props isn’t the answer since you can easily create a parent that changes the props of a child without the use of setState and the child won’t update.


#10

But… yes it will. If you pass a “name” prop to a child component, and then change the prop again, the name will be updated in the child prop. render() runs whenever state changes, or props change. You can stop react from re-rendering unnecessarily, but by default it will re-render. This is how we can update react using Minimongo or redux. Minimongo updates the Meteor wrapper component created by “createContainer”, which updates the data props it passes, which makes the child component re-render.

From react docs:


"When a component’s props or state change, React decides whether an actual DOM update is necessary by comparing the newly returned element with the previously rendered one. When they are not equal, React will update the DOM."


#11

Good catch,

Internally, react-redux does use setState. But technically you could also remount the whole app (or maybe parts of it) by callingReactDOM.render(el, rootNode); on any state change. React would then diff the resulting tree and render only the changes.

My sentence was maybe misleading. setState is used widely in the react-world, but for me it’s not core, because react is about expressing and diffing component-trees in a declarative way.

I now looked at the code of createContainer, it uses the getMeteorData-mixin: https://github.com/meteor/react-packages/blob/2bf88a42c66b4c74df96a95b3953af40afc1d523/packages/react-meteor-data/ReactMeteorData.jsx

this does not use setState, instead, it does a forceUpdate() when the data changes (–> Tracker.autorun is re-run)


#12

Also ready my reply above, but the reason the examples use react’s state is because it is easy and convenient. When using Minimongo, state is managed outside of react, and just given to the react component through props. When the props change, the component updates.

If you want to know how meteor updates its wrapper component however, you will just have to look at the code for createComponet. Summarized, it uses Tracker to track data updates from reactive sources, then forces an update in the wrapper component with the newly updated data.


#13

I had a similar understanding to manuel, so I created a test react app to check what you just said and it didn’t work. I created a “test” component that renders a span with the name prop. App passes this.name as a prop to test. Changing this.name does not cause a update. If I run forceUpdate on the App component, surely, the Test component changes because the prop changed…
React has no way of knowing the prop changed if there is no update


#14

yes, thanks, I understand it now…
@macrozone’s reply also clarified a lot, thanks too.

but adding to my previous reply, it is not the prop change that caused the update, it is the explicit call to forceUpdate that did so. Since the prop is different, the component updates, but the prop checking was triggered by the forceUpdate()


#15

Would you mind posting your sample code?


#16

No it won’t, unless you use setState or forceUpdate somewhere.

Then you’re effectively changing the DOM by hand (at least the root/starting point).

My point in all this is that unless someone has a different way of letting React know the state of the app changed, setState is the core way of changing the state. Let’s not pretend that it isn’t.


#17

But if you just change this.name in the parent component, you are not updating the props, are you? :wink: You are just updating a local variable. Updating the props passed to the child necessitates render() in the parent to run. If you don’t re-render, then the child will not have any different props, hence it can not do prop checking. So yes, changing props WILL make the child re-render, but you actually have to pass the updated props, which I don’t think you are doing.


#18

Here’s an example:

import React, { Component } from 'react';

class Child extends Component {
  render() {
    return (
      <h1>Time: {this.props.time}</h1>
    );
  }
}


export default class App extends Component {

  time = Date.now();

  constructor(props) {
    super(props);
    this.updateTime = this.updateTime.bind(this);
    this.callSetState = this.callSetState.bind(this);
    this.callForceUpdate = this.callForceUpdate.bind(this);
  }

  updateTime() {
    this.time = Date.now();
  }

  callSetState() {
    this.setState({});
  }

  callForceUpdate() {
    this.forceUpdate();
  }

  render() {
    return (
      <div>
        <Child time={this.time} />
        <button onClick={this.updateTime}>Update time</button>
        <button onClick={this.callSetState}>Call setState</button>
        <button onClick={this.callForceUpdate}>Call forceUpdate</button>
      </div>
    );
  }
}

#19

Yup, see my answer above. You are not sending the child the updated variable, as it will only get it when you run render. And the way to trigger render is to do a forceUpdate, change the state (silly way if you don’t have internal state), or re-create the component outside react with new props.

We are really only arguing about very slightly different issues though, I get what you are saying. But concluding that changing props don’t trigger a re-render in the child is incorrect. Changing non-state variables in the parent does not trigger a re-render, that we can agree on.

I think in the end, if the point is that you want to use state that lives Outside of react, you either need to copy that state into a wrapper component’s internal state, or force a re-render with the new data using forceUpdate.


#20

You say you do but the preceding text says you don’t so let’s back up a second.

I’m saying “You have to use setState or forceUpdate to change the state of the app.” to which your answer is close to “You can also update a component’s props.” Okay, can you show an example where you change a component’s props without the use of setState or forceUpdate?