Matthew Tyson
Contributing Writer

How to handle component interaction in React

how-to
Feb 25, 20216 mins

Dealing with component interaction is a key aspect of building applications in React. Here’s a look at the options.

artsy still life of bowling pins with green bowling ball
Credit: Thinkstock

Every React app is composed of interacting components. How these components communicate is an essential aspect of the UI architecture. As applications grow larger and more complex, component interaction becomes even more important.

React provides several methods for handling this need, each with its appropriate use cases. Let’s begin with the simplest approach, parent-to-child interaction.

Parent-to-child with props

The simplest form of communication between components is via properties — usually called props. Props are the parameters passed into child components by parents, similar to arguments to a function.

Props allow for variables to be passed into children, and when values are changed, they are automatically updated in the child, as in Listing 1.

Listing 1. Props (class-based)

<span class="c0">function App(){
</span><span class="c0">  return <div>
</span><span class="c0">    <AppChild name="Matt" />
</span><span class="c0">    </div>
</span><span class="c0">}

</span><span class="c0">function AppChild(props){
</span><span class="c0">  return <span>
</span>      My name is {<span class="c4">props.name</span><span class="c0">}
</span><span class="c0">    </span>
</span><span class="c0">}

</span><span class="c0">ReactDOM.render(<App />, document.getElementById('app'));</span>

Listing 1 shows how props are handled in a function-based component tree. The process is similar with classes. The function-based example shows off the more streamlined syntax of the functional style. You can see this code in action here.

Child-to-parent with function props

Listing 1 allows for values to be passed from parent to child. When a child needs to update the parent as to changes, they cannot just modify properties. Children cannot update props.

If you attempt to directly modify a prop on a child, you will see the following type of error in the console:

<span class="c0">Uncaught TypeError: Cannot assign to read only property 'foo' of object '#<Object>'</span>

Instead, a parent can pass in a functional prop, and the child can call that function. Such functional props are a kind of event-oriented programming. You can see this in action in Listing 2.

Listing 2. Functional props

<span class="c0">function App(){
</span><span class="c0">  const [name, setName] = React.useState("Matt"); 
</span><span class="c0">  return <div>
</span><span class="c0">      <AppChild name={name} onChangeName={()=>{setName("John")}}/>
</span><span class="c0">    </div>
</span><span class="c0">}

</span><span class="c0">function AppChild(props){
</span><span class="c0">  return <span>
</span>      My name is {<span class="c4">props.name</span><span class="c0">}
</span><span class="c0">      <button onClick={props.onChangeName}>Change Name</button>
</span><span class="c0">    </span>
</span><span class="c0">}

</span><span class="c0">ReactDOM.render(<App />, document.getElementById('app'));</span>

Listing 2 introduces useState for managing state. This is a simple mechanism about which you can learn more here. The essence of the functional prop is that when the button is clicked, the function passed in by the App component is executed. Thus, child-to-parent communication is achieved. You can see this code live here.

In general, the concept to keep in mind is this: Props flow down to children, events flow up to parents. This is a valuable design principle that helps to keep applications organized and manageable.

Passing information up to parents

It often happens that child components need to pass arguments up along with their events. This can be achieved by adding arguments to the functional prop callback. This is handled as seen in Listing 3.

Listing 3. Passing arguments to functional props

<span class="c0">function App(){
</span><span class="c0">  const [name, setName] = React.useState("Matt"); //test
</span><span class="c0">  return <div>
</span><span class="c0">      <AppChild name={name} onChangeName={(newName)=>{setName(newName)}}/>
</span><span class="c0">    </div>
</span><span class="c0">}

</span><span class="c0">function AppChild(props){
</span><span class="c0">  return <span>
</span>      My name is {<span class="c4">props.name</span><span class="c0">}
</span><span class="c0">      <button onClick={()=>props.onChangeName("Bill")}>Change Name</button>
</span><span class="c0">    </span>
</span><span class="c0">}

</span><span class="c0">ReactDOM.render(<App />, document.getElementById('app'));</span>

Notice in Listing 3 the line onClick={()=>props.onChangeName("Bill")}. Here we use the arrow syntax to create an anonymous function that includes the argument we want. It’s a simple matter to also pass a variable that is modified by the component itself, with syntax like: onClick={(myVar)=>props.onChange(myVar)}. This code can be seen live here.

As a side note, inline arrow functions as event handlers as seen here are sometimes criticized on the grounds of performance, although this may be overblown.

Function props and React Router

Another important use case is for passing arguments across the React Router. Listing 4 provides an example of how this is achieved.

Listing 4. Passing functional props through Router

<span class="c0">// In the route definition:
</span><span class="c0"><Route path=’/foopath’ render={(props) => <Child {…props} />} />
</span><span class="c0">// In the child component:
</span><span class="c0"><Route appProps={{ onTitleChange }} /></span>

In essence, Listing 4 is allowing for the direct pass-through of the properties by overriding the render of the route.

Sibling communication

The features we’ve seen so far offer the ability to handle sibling communication. This is known in the React docs as “lifting up state.”

The idea here is that when children at the same level of the component tree must share state, that state is pushed up to the parent. The parent then shares the state to the children who need it via props. The children raise events to update that state at the parent, which will automatically be reflected across the shared properties.

React Context API

Another option proffered by React itself is the Context API. The Context API is designed to manage simple, globally interesting values. That is to say, values that are used by many components across the app.

The example given in the docs is a theme setting. Many components will be interested in this setting (in order to reflect the proper theme), which would be very unwieldy to pass around with props.

The Context API is not intended for dealing with complex application data. It is really targeted specifically for avoiding complex prop handling in deeply nested components. A brief example is seen in Listing 5.

Listing 5. Context API

<span class="c0">// defining the context value
</span><span class="c0"><ThemeContext.Provider value="dark">  
</span><span class="c0">// Consuming the context value later on
</span><span class="c0"><Button theme={this.context} />;  </span>

Centralized state with Redux

More complex applications may merit more complex state architectures. The most common library for handling this in React remains Redux. Redux is not simply a centralized store: It is an opinionated and structured eventing system.

The core idea in Redux is that components raise events (known in Redux as actions) via specialized objects called dispatchers. These action events are observed by reducers, which then apply the action to the state. Components in the view are then automatically updated to reflect the state.

You can see from this brief description that Redux introduces quite a bit of complexity and formality into your application. This should be balanced carefully with the benefits of structure and understandability when using Redux.

Other centralized stores

Other approaches exist to managing centralized store, including MobX and rolling your own. Although these solutions may offer advantages over Redux, they must be weighed against the advantage that Redux’s popularity offers, namely familiarity and the availability of developers who understand it.

React offers very powerful and simple component interaction via props and function props. This approach can break down in larger, more complex applications. Leveraging more sophisticated options like the Context API and Redux can address these more complex needs.

Matthew Tyson
Contributing Writer

Matthew Tyson is a contributing writer at InfoWorld. A seasoned technology journalist and expert in enterprise software development, Matthew has written about programming, programming languages, language frameworks, application platforms, development tools, databases, cryptography, information security, cloud computing, and emerging technologies such as blockchain and machine learning for more than 15 years. His work has appeared in leading publications including InfoWorld, CIO, CSO Online, and IBM developerWorks. Matthew also has had the privilege of interviewing many tech luminaries including Brendan Eich, Grady Booch, Guillermo Rauch, and Martin Hellman.

Matthew’s diverse background encompasses full-stack development (Java, JVM languages such as Kotlin, JavaScript, Python, .NET), front-end development (Angular, React, Vue, Svelte) and back-end development (Spring Boot, Node.js, Django), software architecture, and IT infrastructure at companies ranging from startups to Fortune 500 enterprises. He is a trusted authority in critical technology areas such as database design (SQL and NoSQL), AI-assisted coding, agentic AI, open-source initiatives, enterprise integration, and cloud platforms, providing insightful analysis and practical guidance rooted in real-world experience.

More from this author