Direct, Indirect, Redirect: React Props, Context, and State as Inputs

Ryan Kahn (he/him) - Dec 6 '19 - - Dev Community

tl;dr

Functions are about inputs, side effects, and outputs. React component functions are no different. How do you think about props, state, and context in terms of inputs, side effects, and outputs? What follows is a model I find useful in talking with students. I'd love to hear how it sits with you, or if it brings up any questions!

Intro

With the latest versions of React (>16.8), it's possible to model all aspects of a client application as a problem of functions and composing functions. Hooks provide a basic functional abstraction for state, side effects outside of the render cycle, and retrieving data from various contexts in the React tree.

Functions are a set of inputs, side effects, and an output. React introduces many new ideas, but they still map well to more basic ideas about how functions work. Let's take a look at how that mapping looks!

Props: Direct Input

Props are the direct inputs into a function. They are passed in React together as an object argument to the component function.

Here we see an example of a "Score" display in Typescript that takes a number prop called score. It renders that score into a string, and that string into a span element. Eventually, our inputs will be represented in some form in the output of our combined component functions, and in the rendered result!

// score.tsx

import React from 'react'

export function Score({ score: number }) {
  return <span>Your score is {score}</span>
}
Enter fullscreen mode Exit fullscreen mode

Context: Indirect Input

Context is another available input into our React component functions. Where as props are direct, context is an indirect way to pass data to our components. With context, we pass a value once as a prop to a provider above our components in the React tree. This provider then, through React, passes the value to its consumers within our components.

Here is an example in Typescript along the same lines, with a GameHeader pulling a Game object from the context, and rendering a the score. Notice how the same value passes from context, and becomes a prop. We'll see how to update this value later when talking about state!

// game.tsx

import React from 'react'

export type Game = { score: number }

export const GameContext = React.createContext<Game>({ score: 0 })
Enter fullscreen mode Exit fullscreen mode
// game_header.tsx

import React from 'react'
import Score from './score'
import {GameContext} from './game'

function GameHeader() {
  const { score } = React.useContext(GameContext);

  return <header><Score score={score} /></header>
}
Enter fullscreen mode Exit fullscreen mode

State: Redirect Input

Finally, we have state. State is also an input, but it is also a side effect. That's why the hooks for state, useState and useReducer, both return a tuple of a value and a function. The value is the input, and the function performs the side effect.

Hint: When you see a function that doesn't return a value (in Typescript something like () => void) chances are it performs a side effect. That's not to say that functions that return values can't perform side effects, but those that don't most likely either do nothing or perform a side effect.

The side effect in this case triggers the component to re-render and receive the updated value. This allows you to redirect values within your application. Here we see a more complete example, where the score is stored, and updated each time the user clicks a button:

// app.tsx

import React from 'react'
import {GameContext} from './game'
import {GameHeader} from './game_header'

function App() {
  const [game, incrementScore] = React.useReducer(({ score }) => ({
    score: score + 1
  }), { score: 0 });

  return (
    <GameContext.provider value={game}>
      <GameHeader />
      <button onClick={() => incrementScore()}>Click Here!</button>
    </GameContext.provider>
  )
}
Enter fullscreen mode Exit fullscreen mode

Notice how the side effect function ends up composed into our output (in the onClick handler) along with the value. That side effect redirects the click event into our stateful value, and re-inputs that into our component tree. Cool!

So how does this fit into your mental framework? I'd love to hear!

. . . . . . . . . . . . . . . . . . . .
Terabox Video Player