**All of the code in this post is going to be using JavaScript ES6 & class-based components for React.  

One of the topics I commonly find people struggling with in regards to React is how to keep track of and communicate data throughout their application. This is probably partially due to how different a React component (with their entwined state and logic) can feel from a traditional framework's view controllers. This is a really core concept to how React is intended to be structured and having a strong understanding of it enables you to build applications that work smoothly. I'm going to break this down into three general groups: 

  • Pre-render parent-child communication
  • Post-render parent-child communication
  • Sibling communication

This post is going to cover the first group, pre-render parent-child communication.


State & Props

The most common and widely used form of communication in React involves the state of your component and the properties (or props) that are passed down to child components. State is a component level concept, you can essentially think of it as the private data of your component. In the example below we'll define some initial state in our constructor function and make use of it in our render function through {this.state...}

**You can live-edit any of this code by the way.

It's important to note that you should never mutate state directly by assigning to this.state elsewhere in your app, rather, changes to the state of your application are triggered with React's setState() function. This is also the primary way that you will re-render the UI of your application as calls to setState trigger UI updates.

The basic use of setState is with simple key-value pairs, for example we could change the state of greeting in the previous example with a call to setState in some handler:  

this.setState({
  greeting: 'Welcome!'
})

This greeting would then be immediately updated in our UI anywhere that we have {this.state.greeting}. Knowing that state represents a consistent source of internal data for your component, and that this data is always reflected in your UI, quickly frees you to start building some pretty cool things. 

Here is something a little more advanced, we'll use a random hex code generator to set the state of this.state.color to a random color every 2.5 seconds and hook the state to an inline style to update the color of the block on each render:

While this is useful, odds are our React app is going to be built out of more than one component, and if it isn't we need to seriously reconsider what we're doing. So we need ways to communicate data across components, luckily React provides several.

I'm going to start out by discussing props. Props are the primary source of data communication in React and they operate in a unidirectional top-down manner. Parent components can pass props down to child components and child components down to their children, etc. Props, much like state, are plain JavaScript objects and should always be treated as immutable. They essentially serve to pass down configuration and functionality to React components further down the tree. They are commonly hooked to the state of a parent component (so that changes to the state of a component propagate down to its children), but don't necessarily have to be. They are pretty easy to understand once you see them in action, here is a basic example to help illustrate:

If you are looking to communicate some information back up to the parent, this is most often done with callbacks. We pass a function reference to our child components as props, which gives them the ability to notify their parent components that a state change or some other data handling needs to occur & the corresponding function should be executed. 

Here is a basic counter app as an example of this:

These examples are often what is referred to as Smart-Dumb component architecture. Your 'smart' components are responsible for the state of their component hierarchy, responding to events, processing data and passing along the necessary info to their children. While the 'dumb' components are strictly used to render the resulting data to the UI, the only logic they should really be implementing (if any at all) relates to the props that they receive from their parents. This is largely enabled by the fact that both props and state are deterministic, that is, for a given set of props and state the resulting output should always be the same.

This also leads to a pretty common shorthand way of representing 'dumb' components known as stateless functional components, where a React component whose only responsibility is rendering can be expressed as a basic function. 

This is the previous example with the child components refactored to be stateless functional components:

It's important to note that this isn't strictly necessary, but rather a recommendation from the React team as a means to help further improve the performance of React going forward. Although I personally find them helpful in quickly identifying a pure render component vs a class based component in my code, they have also become pretty common out in the wild so I wanted to give a brief overview here.


Context

And now we move on to some of the alternative ways of communicating in React. I'm going to start off by saying, before reaching for another tool in the shed consider whether your problem can be solved with props and state, even if you have to refactor things a little, because honestly 90% of use cases in React can be solved by correctly using props and state.

With that being said, context in React is similar in concept to global variables. If you have a situation in which you want to pass data down through some sub-tree of components without having to manually pass that data through props at every given step, context lets you do so. The most common use cases for this are things that would normally be represented globally like styles or a logged in user. Context involves a top-level context-provider that defines what is available to any child components if they ask for it. This is done by initializing some context values through the use of getChildContext(), along with defining some class properties on the context-provider and the context-user which are done with childContextTypes, and contextTypes respectively. 

It's a little clearer to see how this looks in practice:

It's important to note that getChildContext() can be updated by state changes, like just about everything in React, which again allows you to use it most effectively for things that are by nature global (or at least global to a given sub-tree), like application settings.

Here we'll take the previous example but add some state to the User component that determines the value of this.context.favColor:

Stateless functional components also have access to context in your application as the second argument to the function, provided that contextTypes is defined as a property of the function.

Here is one last simple example to illustrate: 

Again, just to emphasize, don't overuse context (or refs, which we'll cover next time). One of the most valuable things that you get from working with props and state for your parent-child communication is a very explicit data flow. You know, when looking at any given component, where its data came from and where its data is going, which is priceless as applications continue to grow. Don't be completely afraid to use the other methods available to you, just be sure to take stock and know when they best suit your needs.

That's it for part 1, In part 2 I'll cover post-render parent-child communication by referencing your rendered DOM elements with the React life-cycle method componentDidMount() using both vanilla JavaScript and a convenient React feature known as refs.

 

-Brad

Comment