Today I Learned

ins8s

26 posts about #react

Access The Latest Lifecycle Methods In An Old App

With the release of React 16.3 we have access to some new lifecycle methods and are in the first phase of what will eventually result in the deprecation and removal of componentWillMount, componentWillReceiveProps, and componentWillUpdate.

You may not be ready to move your project to React 16.3, but that doesn’t mean you can’t start migrating your lifecycle methods now. We’ll need a polyfill — react-lifecycles-compat.

import React from 'react';
import { pollyfill } from 'react-lifecycles-compat';

class MyComponent extends React.Component {
  static getDerivedStateFromProps(nextProps, prevState) {
    // ...
  }

  render() { ... }
}
polyfill(MyComponent)

export default MyComponent;

For any of our class components for which we’d like to start using the new lifecycle methods, we just need to import the polyfill function and then transform the class component with the polyfill before exporting it.

source

Debug Jest Tests In create-react-app

When you put a debugger; statement in one of your Jest tests and run yarn test, the test runner will ignore the debug statement and run to completion. This is because Jest defaults to parallelizing tests which won’t mix well with manual debugging intervention.

If we want to be able to run our Jest tests through a debugger. We will need two things. First, we need a debugging environment — Chrome’s devtools will work well for this. Second, we need our tests to run in band. The react-scripts documentation recommends adding a second, debug-specific test command to your package.json:

"scripts": {
  "test:debug": "react-scripts --inspect-brk test --runInBand --env=jsdom"
}

You can now run yarn test:debug which will start a paused debug session. Open chrome at chrome://inspect to access and open the debugging session panel. Now, debug away.

Building A React App In The Browser

There are a couple relatively new tools that give you just about everything you need to build a React app in the browser.

The first is CodeSandbox. It has a couple pre-configured environments for different technologies. The main one is React. CodeSandbox describes itself as such:

an online editor that helps you create web applications, from prototype to deployment.

The second, which just released out of beta, is Glitch which comes with projects like react-starter so that you can jump into a React project that is ready to roll. Glitch describes itself as:

the friendly community where you’ll build the app of your dreams

You can quickly build and publish React apps in either of these and share them with the world.

Test Files In create-react-app

Any .js files placed in the __tests__ directory will be treated as tests by Jest when running yarn test. If you don’t want to place all of your files in that directory and especially if you want to co-located your test files with the source files, you can name them with the .test.js or .spec.js suffixes.

Any files in your create-react-app project ending in these suffixes will be treated by Jest as test files and included in test runs.

There are more details in the docs.

Enforce Specific Values With PropTypes

Being able to constrain our user interfaces to very specific values is valuable. This makes our interfaces easier to reason about and easier to test. PropTypes in general are one of the ways that we constrain our UIs. We can go even further than simple type constraints by limiting a prop to a specific set of values, an enum if you will.

MyComponent.propTypes = {
  flavor: PropTypes.oneOf(['Vanilla', 'Chocolate', 'Strawberry']),
};

The docs say about oneOf():

You can ensure that your prop is limited to specific values by treating it as an enum.

If we use MyComponent with a value such as Pistachio, we’ll have a console warning to answer for.

source

A Component Is Just A Bag Of Data

If you write enough React using JSX, it is easy to forget that you’re not working with markup. Everything — divs, h1s, 3rd party components, your components — all get boiled down to JavaScript objects full of data.

Any given React component is really just a bag of data. Try doing a console.log to see. Here is an example from an earlier post.

const ParentWithClick = ({ children }) => {
  return (
    <React.Fragment>
      {React.Children.map(children || null, (child, i) => {
        console.log(child);
        return <child.type {...child.props} key={i} onClick={handleClick} />;
      })}
    </React.Fragment>
  );
};

const App = () => (
  <div>
    <ParentWithClick>
      <span>Click this span</span>
    </ParentWithClick>
  </div>
);

Looking in the console, we see the following output:

Object {type: "span", key: null, ref: null, props: Object, _owner: Object…}
 type: "span"
 key: null
 ref: null
 props: Object
  children: "Click this span"
 _owner: Object
 _store: Object

It contains information about the component itself and because of the tree structure of this data, you could potentially expand the props —> children sections several times for certain components.

See a live example here.

Mapping Over One Or Many Children

In Dynamically Add Props To A Child Component, I talked about how a child element can be reconstituted with additional props. The approach I showed will only work in the case of a single child being nested in that component. What if you want your component to account for one, many, or even children?

React comes with a built-in function for mapping that handles these cases.

const ParentWithClick = ({ children }) => {
  return (
    <React.Fragment>
      {React.Children.map(children || null, (child, i) => {
        return <child.type {...child.props} key={i} onClick={handleClick} />;
      })}
    </React.Fragment>
  );
};

The React.Children.map function allows mapping over one or many elements and if children is null or undefined, it will return null or undefined respectively.

See a live example here.

Dynamically Add Props To A Child Component

If your component has an element nested in it, then it will receive a children prop. There are a number of things you can do beyond simply including the children as part of the rendered output of the component. One thing you can do is put additional props on the child.

const ParentWithClick = ({ children }) => {
  return (
    <children.type
      {...children.props}
      onClick={() => alert("You clicked me!")}
    />
  );
};

This ParentWithClick component will reconstitute its child component with its given props and a new onClick prop.

Here is how it can be used:

const App = () => {
  return (
    <ParentWithClick>
      <span>Hello!</span>
    </ParentWithClick>
  );
};

Click on Hello! and you’ll see the alert.

Minor caveat: multiple children and a string child will need to be handled differently.

See a live example here.

Who Is Your Favorite Child?

When we put some content inside the open and close tags of one of our components, we get access to it as the children prop.

const Parent = ({children}) => {
  return (
    <React.Fragment>
      <p>These are my favorites:</p>
      {children}
    </React.Fragment>
  );
}

const App = () => (
  <div>
    <Parent>
      Greg and Marsha
    </Parent>
  </div>
);

What happens if we also provide an explicit children prop to Parent?

const App = () => (
  <div>
    <Parent children={"Jan and Peter"}>
      Greg and Marsha
    </Parent>
  </div>
);

Which will take precedence when we destructure children in the parent component?

In the example above, we’ll still see Greg and Marsha rendered. The content placed inside the tags will take precedence over the explicit children prop.

See a live example here.

Use withRouter To Pass Down React-Router History

A standard way to navigate with react-router besides using the Link component is to call history.push. Components that are directly rendered by a Route will have access to this and other router props. But what about other components?

The withRouter HOC gives us direct access to a history prop.

import React from 'react';
import { withRouter } from 'react-router';

const SpecialButton = withRouter(({ history, path, text }) => {
  return (
    <Button
      onClick={() => { history.push(path); }}
    >
      {text}
    </Button>
  )
});

This special button component is given the history prop via the withRouter HOC along with any props that we directly pass it. With that we are able to directly invoke a route change using history.push().

Read Only Input Elements

Here is an input element with a value and no onChange handler.

const MyInput = ({ value }) => {
  return (
    <input value={value} />
  );
};

React will raise a warning regarding the input element because it has a value without an onChange handler leaving React to wonder if it is intended to be a controlled or uncontrolled component.

If our intention is to have the value set but not allow the user to directly change it, we just need to let React know that.

const MyInput = ({ value }) => {
  return (
    <input readOnly value={value} />
  );
};

The readOnly prop means we don’t intend for the input to be modified by user input. The React warning will now go away.

h/t Dillon Hafer

Destructure Variables As Props To A Component

When passing down props, a redundant-feeling pattern can sometimes emerge.

const MyComponent = ({ handleChange, handleBlur }) => {
  return (
    <div>
      <OtherComponent />
      <MySubComponent handleChange={handleChange} handleBlur={handleBlur} />
    </div>
  )
};

The typing feel duplicative, as if there ought to be a better way. One option is to simply pass down all the props:

<MySubComponent {...props} />

This approach may result in passing down props that we don’t intend to pass down and clutters the flow of data in our app.

Here is another approach:

const MyComponent = ({ handleChange, handleBlur }) => {
  return (
    <div>
      <OtherComponent />
      <MySubComponent {...{handleChange, handleBlur}} />
    </div>
  )
};

Here we are taking advantage of two ES6 features. Since the naming is the same, we can use property shorthands. Then we immediately use the spread operator to splat it back out as the props to the component.

h/t Vidal Ekechukwu

Alter The Display Name Of A Component

Components adopt their display name from the class or function that renders them. A component’s display name becomes important to know about as soon as you start digging through the React devtools interface — whether debugging or just perusing the component hierarchy, the display names of components is what you’ll see. In most circumstances, the display name is good enough as is. If you want or need to, you can change it.

const Hello = ({ name }) => {
  return (
    <h1>{name}</h1>
  );
};
Hello.displayName = "Hola";

By setting the displayName property on this component, you are able to alter what name is used by React devtools.

This can be useful when bringing in a 3rd party library or component that doesn’t use a display name that you find helpful — in particular when using Higher Order Components.

Visually Select A React Element For Inspection

Similar to the Elements tab of Chrome devtools, the React devtools extension provides a visual element selector to make it easier to inspect an element you can see in the browser.

select and inspect a react component

Open the React devtools, click the crosshair icon, hover around the browser until the element you are looking for is visually highlighted, and then click. The React component hierarchy will be expanded to reveal that element. You can now inspect it or quickly navigate to nearby elements.

Defining State In A Simple Class Component

Most class components start off with a constructor which does some initialization of the component including setting the components initial state. It might look something like the following:

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      loading: true
    };
  }

  render() {
    if (this.state.loading) {
      return (
        <p>Loading...</p>
      );
    } else {
      // ...
    }
  }
}

If setting state is the only thing you need to do in the constructor, then you can skip the constructor all together.

class MyComponent extends React.Component {
  state = {
    loading: true
  };

  render() {
    if (this.state.loading) {
      return (
        <p>Loading...</p>
      );
    } else {
      // ...
    }
  }
}

This second example will work the same as the first, and it is a bit more concise.

Use A Ref To Autofocus An Input

When creating highly interactive interfaces with React, we are trying to make the user’s experience of our app as smooth as possible. This means that when an edit button reveals an input field, we want that field to be in focus so that the user can immediately start typing.

This is a great use for React’s ref prop. When you supply your component with a function as the ref prop, that function will be called with a reference to itself on mount and with null on unmount.

class MyAutofocusInput extends React.Component {
  focusInput = (component) => {
    if (component) {
      component.focus();
    }
  };

  render() {
    return (
      <input
        ref={this.focusInput}
        value={this.props.value}
        onChange={this.props.onChange}
      />
    );
  }
}

When this component gets rendered, the input will be focused via our focusInput function.

Note: refs only work with class components, so don’t try to use it with a functional component.

See Refs and the DOM in React’s documentation for more details.

Quickly Search For A Component With React DevTools

As the size of your React app grows, it can become challenging to track down specific components within the React DevTools extension. You opened it up with the hopes of quickly inspecting the props being received by a component, but find yourself navigating through the DOM structure instead.

The React DevTools extension provides a search bar that can be used to quickly filter out most components.

react devtools component search

The search bar is located at the top of the extension. Once you start typing the results are immediate. Then, if you hover over any of the remaining components, you’ll get some visual feedback as they are highlighted in the browser.

Dispatch Anywhere With Redux

Your React app is going to have a single top-level store which is connected to the app with the Provider component. Most of the time, when you create a connected component, you’ll create prop functions that dispatch on a redux action.

This isn’t the only place you can dispatch though.

If you export your store, then it can be imported anywhere along with its dispatch function.

// src/index.js
export const store = createStore(rootReducer);
// src/components/MyComponent.js
import { store } from '../index';
import { updateData } from '../actions';

// ...

  componentDidMount() {
    getData().then((json) => {
      store.dispatch(updateData(json));
    }
  }

See the dispatch documentation for more details.

Inactive And Active Component Styles With Radium

Radium is “toolchain for React component styling” allowing you to do comprehensive inline styling with CSS.

Often times, especially in the case of a series of nav elements, there is a need to style one element as active while styling the rest as inactive. This can be achieved with Radium by defining two groups of styles (base and active) and then relying on props to conditionally apply the active style.

import React from 'react';
import Radium from 'radium';

const styles = {
  base: {
    textDecoration: "none",
    color: "gray",
  },
  active: {
    color: "black",
    backgroundColor: "lightgray",
  },
};

let NavItem = ({ label, path, active }) => {
  return (
    <a
      href={path}
      style={[
        styles.base,
        styles[active && 'active'],
      ]}
    >{label}</a>
  );
};

NavItem = Radium(NavItem);

With Radium, our base (inactive) styles always get applied. Then, the active styles only get applied when the active prop is true. We produce a Radium-ified version of our NavItem on the last line so that Radium can handle all of the styling of the component.

Rendering Multiple Nodes With Fragments

When rendering, React expects a component to only return a single node. The DOM hierarchy of most components will easily follow this rule, but what about those components that do have multiple inline nodes?

The two solutions have been to either wrap the inline nodes in an outer div or to return them as a comma separated array of nodes. With React 16, we can avoid the deficiencies of both of these approaches by using a fragment.

Just wrap your inline nodes with a React.Fragment node. React will understand your JSX without wrapping the output DOM in a superfluous div.

render() {
  return (
    <React.Fragment>
      <p>Name: {firstName} {lastName}</p>
      <p>Email: {email}</p>
      <p>Age: {age}</p>
    </React.Fragment>
  );
}

See the docs on fragments for more details.

Access Env Vars In create-react-app

Environment-specific configurations are an important part of most applications. You can access environment variables in your create-react-app code using process.env.

There are a couple built-in environment variables, such as NODE_ENV. Anything custom that you want to provide must be prepended with REACT_APP_. If it isn’t, that environment variable will be ignored with no warning.

The following line of code

const base_api_url = process.env.REACT_APP_BASE_API_URL;

will have access to whatever that value is in the environment when the server is started or the app is built.

Set that value inline like so:

REACT_APP_BASE_API_URL="https://api.my_app.com" yarn start

source

Proxy To An API Server In Development With CRA

create-react-app is a great way to bootstrap a React project, especially if you are building a single-page app. When building an SPA, you more likely than not will have a backend API that you interact with.

You can set up your React app to interact with that backend API server in development using the proxy configuration in package.json.

// package.json
  ...
  "proxy": "http://localhost:4000",
}

This will allow you to keep your API calls nice and clean.

fetch("/api/session", ...

No need to manage some sort of host URL environment variable.

Additionally, this will remove an CORS issues because the webpackDevServer will be proxying any paths that it doesn’t recognize to the host and port that you’ve specified.

See the create-react-app docs for more details.

Inline Style Attributes Should Be Camel Cased

When adding a few quick styles to React components, you can add it directly on the tags in the JSX. To do this, use the style tag with a plain old JavaScript object of styles.

<div style={{ padding: "1em", color: "#fff" }}>

If you are using a CSS attribute that is normally hyphenated like padding-top or background-color, you’ll need to camel case it in the JSX.

<div style={{ paddingTop: "1em", backgroundColor: "#fff" }}>

This is because our styles now need to conform to JavaScript syntax rules since they are in the form of a POJO.

Read the documentation for more details.

Passing Props Down To React-Router Route

When using react-router, you’ll often use the component prop to have a certain component rendered.

<Route
  path="/my/path"
  component={MyComponent}
/>

If, however, you need to pass props down into MyComponent, then you’ll want to use the render prop with an inline function.

<Route
  path="/my/path"
  render={(routeProps) => (
    <MyComponent {...routeProps} {...props} />
  )}
/>

The two spread operator statements are essential. They will pass down the route props that Route would have passed down plus the additional set of props that you want to pass down.

Force A Component To Only Have One Child

A component can normally have an arbitrary number of elements nested directly inside it. React’s Children.only function can be used to force it to a single direct child.

import React, { Children, Component } from "react";

class App extends Component {
  render() {
    return (
      <SingleChildContainer>
        <span>There can only be one!</span>
      </SingleChildContainer>
    );
  }
}

const SingleChildContainer = props => {
  return Children.only(props.children);
};

export default App;

The React docs describe the behavior of Children.only as such, “Returns the only child in children. Throws otherwise.”.

If you modify the return in App to contain the following JSX

<SingleChildContainer>
  <span>There can only be one!</span>
  <div>What about me?!</div>
</SingleChildContainer>

then an error will be thrown (React.Children.only expected to receive a single React element child).

The Provider component in react-redux is an example of where this is used.