Today I Learned

hashrocket A Hashrocket project

114 posts about #react surprise

convice typescript your literally in the union

We want to pass a string prop that is defined as a union.

function Alert({
  level,
  children,
}: {
  level: "info" | "warn";
  children: ReactNode;
}) {
  const iconName = { info: "info", warn: "alert-triangle" }[level];
  return (
    <View>
      <Feather name={iconName} />
      {children}
    </View>
  );
}

info and alert-triangle are both in the union. Should be groovy, right?

❯ npx tsc
src/components/Alert.ts:666:69 - error TS2322: Type 'string' is not assignable to type '"key" | "type" | ... 267 more ... | undefined'.

Well shoot.

We need to convince typescript that this is no average string. Enter const assertions

const iconName = { info: "hashrocket" as const, warn: "alert-triangle" as const }[level];

And now we get a beautiful error:

❯ npx tsc
src/components/Alert.ts:666:69 - error TS2322: Type "hashrocket" | "alert-triangle" is not assignable to type '"key" | "type" | ... 267 more ... | undefined'.

Change the name back to "info" and we get no errors, with good reason this time. 😎

Expo Go over VS Code Live Share shared servers

VS Code Live Share is a pretty sweet way to remote pair. It supports Shared Servers, a fancy way to forward ports without exchanging ssh keys. Unlike ssh port forwarding, however, there isn't a way for the joiner to choose which of their local ports will be used. The port selection is random.

That doesn't cut it with Expo Go (and maybe vanilla react-native?) which demands the port be the same for the server and the client. So I looked for a way to forward one port on my local machine, the random one chosen by Live Share, to another port on my machine - the expo default of 19000.

Thanks to socat that was easy:

 socat tcp-listen:19000,reuseaddr,fork tcp:localhost:<random liveshare port>

I opened exp://localhost:19000 in Expo Go in the simulator. 🤘 Sweet.

Proxy Development Requests with Create-React-App

Create-React-App supports an optional proxy option in development, which can be added to your package.json. In my case, I was dealing with CORS issues locally because our frontend and backend run on the same server in production, but our development setup is different.

My backend was running on http://localhost:4000, and frontend on http://localhost:3000.

In my package.json, I specified that I wanted to proxy my requests to backend development server:

{
  ...
  "proxy": "http:localhost:4000"
  ...
}

Then I changed my fetch call to be a relative URL:

// Was fetch("http://localhost:4000/api/graph", ...params)
fetch("/api/graphql")

Create-react-app will recognize that this path is not a static asset and "will proxy the request (http://localhost:4000/api/graphql) as a fallback".

Create-React-App Docs - Proxying API Requests in Development

React SyntheticEvent for Focus and Blur

Some JavaScript events like focus and blur do not bubble to parent elements. Let's say that we have:

<div
  onfocus="console.log('div focus')"
  onblur="console.log('div blur')"
>
  <input
    type="text"
    placeholder="Type something here"
    onfocus="console.log('input focus')"
    onblur="console.log('input blur')"
  >
</div>

Then if we focus we'll see in the console input focus and if we blur we'll see input blur. This makes a lot of sense as a div does not have the concept to focus, there's no blinking cursor or anything like that.

But in React the scenario is different. In an attempt to standardize all browser events to behave similarly to each other and across different browsers, React wraps all events in SyntheticEvents and for the onFocus and onBlur events we'll see that they do bubble up:

<div
  onFocus={() => console.log('div focus')}
  onBlur={() => console.log('div blur')}
>
  <input
    type="text"
    placeholder="Type something here"
    onFocus={() => console.log('input focus')}
    onBlur={() => console.log('input blur')}
  />
</div>

Here if we focus we'll see input focus and div focus in this order and if we blur we'll see input blur and div blur in this order again.

Use reduced motion to control a video

I used my useWatchMedia hook to play/pause a video based on the reduced motion OS config.

On Macs you can set this option in System Preferences => Accessibility => Display:

image

This way users with motion sickness can have better time browsing your website.

And here's my video component:

const useReducedMotion = () => {
  return useWatchMedia('(prefers-reduced-motion: reduce)');
};
const MyVideo = ({url}) => {
  const ref = useRef();
  const reducedMotion = useReducedMotion();

  useLayoutEffect(() => {
    if (ref.current) {
      reducedMotion ? ref.current.pause() : ref.current.play();
    }
  }, [ref, reducedMotion]);

  return (
    <video autoPlay loop muted playsInline ref={ref} src={url} />
  );
};

As the browser exposes this config via media query we could also use regular css for that media to enable/disable features, for example css animations.

Watch Media query changes using React hooks

I end up creating a React hook to watch a media query change. I am using window.matchMedia to achieve that. Check this out:

const useWatchMedia = (media) => {
  const [matches, setMatches] = useState();

  useEffect(() => {
    const watchedMedia = window.matchMedia(media);
    const mediaListener = () => setMatches(watchedMedia.matches);

    mediaListener();
    watchedMedia.addListener(mediaListener);

    return () => watchedMedia.removeListener(mediaListener);
  }, [media]);

  return matches;
};

React hooks are an excellent way to share this setup (and cleanup)!

React `useImperativeHandle` hook

I end up using useImperativeHandle hook to extract a component to be reusable. In this case I am using react-google-recaptcha lib for captcha and I have to pass a ref so the lib can bind some functions to that. Let's see how I got to expose a function to the parent component via ref:

import React, {useRef, useImperativeHandle, forwardRef} from 'react';
import ReCAPTCHA from 'react-google-recaptcha';

const Captcha = (_props, ref) => {
  const captchaRef = useRef();

  useImperativeHandle(ref, () => ({
    executeAsync: () => {
      captchaRef.current.executeAsync();
    },
  }));

  return (
    <ReCAPTCHA ref={captchaRef} sitekey="some key here" />
  );
};

export default forwardRef(Captcha);

I had to use both useImperativeHandle and forwardRef in order to receive the ref from the parent and "forward" to the ref internal to this component.

And here's how I am calling that:

const MyForm = () => {
  const captchaRef = useRef();

  const onSubmit = async (values) => {
    const recaptchaToken = await captchaRef.current?.executeAsync();
    placeOrder({recaptchaToken, ...values});
  };

  ...

  return (
    <form onSubmit={onSubmit}>
      ...
      <Captcha ref={captchaRef} />
      <input type="submit" value="Save" />
    </form>
  );
};

Although this is a very unusual thing to do in React, it was necessary in order to extract this component.

Disallow Large Jest Snapshots Using ESLint

eslint-plugin-jest has a handy rule to limit the size of snapshots - no-large-snapshots. This is especially useful for maintainability of snapshot tests. Snapshots of a large component are cumbersome to maintain as it requires the dev to have a deep knowledge of this components DOM and contained logic.

Github - eslint-plugin-jest/no-large-snapshots

Bonus Round: React Native has updated their documentation and added some great testing guidelines for mobile apps.

Generic React Components

When props are generic like this:

inteface SelectProps<T> {
  options: T[];
  onChange: (value: T) => void;
}

function CoolSelect<T> (props: SelectProps<T>) {
	// ...
}

The generic part can be specified in JSX like this:

interface Fruit {
  name: string;
  isFruit: boolean
}

const fruits = [
  { name: 'Pumpkin', isFruit: true },
  { name: 'Avocado', isFruit: true },
  { name: 'Cucumber', isFruit: true },
  { name: 'Bell Pepper', isFruit: true },
]

funciton App() {
  return <CoolSelect<Fruit> options={fruits} />
}

See it? <CoolSelect<Fruit> options={fruits} />

Now when crafting the onChange function in this example, it's type will be infered as this:

type OnChange = (value: Fruit) => void
funciton App() {
  return (
    <CoolSelect<Fruit>
      options={fruits}
      onChange={value => {
        if (value.isFruit && value.name === 'Bell Pepper') {
          console.log("You're blowing my mind dude!")
        }
      }}
    />
  )
}

*This syntax is available in Typescript v2.9+

Use Enzyme's `wrappingComponent` option in `mount`

Also works with shallow:

const provided = {super: 'cool', object: ['of', 'things']};

// This means that the root of the tree is going to be the provider
describe('Some Component', () => {
  it('does cool things when props change', () => {
    const target = mount(
      <CoolProvider thing={provided}
        <Component changeMe={false} />
      </CoolProvider>
  })
})

// This means that the root of the tree will be your Component
describe('Some Component', () => {
  it('does cool things when props change', () => {
    const target = mount(<Component changeMe={false} />, {
      wrappingComponent: CoolProvider,
      wrappingComponentProps: {super: 'cool', object: ['of', 'things']}
  })
})

This pattern is particularly meaningful when you want to call setProps or other methods that are only valid on the root of the component tree, because dive can't help you there. If you want to change the props on both, you can use target.getWrappingComponent() to get at the wrapping component in the same way!

https://enzymejs.github.io/enzyme/docs/api/ReactWrapper/getWrappingComponent.html

Expo PushNotifications with pop-ups on Android

Expo has an amazing push notification service. In order to display push notifications in a pop-up style (the default on iOS) on Android, you must do two things:

  1. Create an Android push notification channel
  2. Set the priority and channelId in the push notification payload to expo.

Create channel example:

import React from "react";
import { Platform } from "react-native";
import { Notifications } from "expo";

class App extends React.Component<Props> {
  componentDidMount() {
    if (Platform.OS === "android") {
      Notifications.createChannelAndroidAsync("chat-messages", {
        name: "Messages",
        priority: "max",
        sound: true,
        vibrate: [0, 250, 500, 250]
      });
    }
  }

  render() {
    // ...
  }
}

Send PN with priority and channel example:

curl -H "Content-Type: application/json" -X POST "https://exp.host/--/api/v2/push/send" -d '{
  "to": "ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]",
  "title":"New Message",
  "body": "Are you there?",
  "channelId": "chat-messages",
  "priority": "max"
}'

Prefetch images in ReactNative

When a react native app boots and there is a known set of user/group avatar urls received from an API, it may be a better user experience to load them before hand.

React Native's Image component has a prefetch method that will save the image into the device's image cache:

import { Image } from "react-native";

const App = () => {
  const { users, loading } = useUserFetcher();

  users.forEach(u => Image.prefetch(u.avatarURL));

  return (
    <View>
      <UserList users={users} loading={loading} />
    </View>
  );
};

RN fetch on Android requires mime type

ReactNative on Android requires a mime type when uploading files with FormData. But the only way to get the mime type of a user-chosen file is to read the file or files into memory after a user select one or more. In all of my use cases, my server didn't care if the mimetype was in the FormData.

The easy solution is to just set the mimetype to a binary file type (e.g. application/octet-stream):

let data = new FormData();
data.append("file", {
  name: "cool.pdf",
  uri: "///files/cool.pdf",
  // MIME-type required on Android
  type: "application/octet-stream"
});

fetch("http://example.com/upload-file", data);

💅 styled-components limit prop names

💅 styled-components is one of my absolute favorite libraries! ❤️ I just have to be careful not use prop names for my components that could be interpreted as styles.

For example:

interface Props {
  position: "left-side" | "right-side";
  item: Item;
}

const ListItem= styled.View<{position: "left-side" | "right-side"}>(({position}) => ({
  backgroundColor: position === "left-side" ? "burlywood" : "skyblue";
}));

const ListItemContainer: React.FC<Props> = ({position, item}) => {
  return (
    <ListItem position={position}>
      <Text>{item.name}</Text>
    </ListItem>
  );
}

<ListItemContainer item={item} position="left-side" />

This will return an error because in StyleSheet, position must be one of absolute or relative

How to typescript react native list refs

VirtualLists (i.e. SectionList, FlatList) have always been dificult for me to describe in typescript, but the following seems to work:

interface Props {
  list: MutableRefObject<SectionList<any> | undefined>;
}

Example:

import React, { MutableRefObject } from "react";
import { SectionList, View, TouchableOpacity, Text } from "react-native";

interface Props {
  listRef: MutableRefObject<SectionList<any> | undefined>;
}

const Button = (props: Props) => {
  return (
    <TouchableOpacity
      onPress={() => {
        props.listRef.current?.scrollToLocation({
          itemIndex: 0,
          sectionIndex: 0,
          animated: true
        });
      }}
    >
      <Text>Scroll Up</Text>
    </TouchableOpacity>
  );
};

const Screen = () => {
  const listRef = useRef<SectionList<any>>();

  return (
    <View>
      <SectionList ref={listRef} />
      <Button listRef={listRef} />
    </View>
  );
};

Import GraphQL Queries Where You Need Them (CRA)

If you want to export/import your GraphQL queries in your Create-React-App, you'll need to add a library to make it work. You can use a library called grapql.macro to use .gql or .graphql files in your app.

If you're also using jest, you'll need to add jest-transform-graphql and update your jest configuration to pick up these file types.

Source: https://www.apollographql.com/docs/react/integrations/webpack/#jest

React Testing Library => within nested queries

Wow, React Testing Library has a within helper to get nested searches on the dom. Check this out with this example:

const DATA = {
  movies: ["The Godfather", "Pulp Fiction"],
  tv: ["Friends", "Game of Thrones"],
};

const MyContainer = () => (
  <>
    {Object.keys(DATA).map(category => (
      <MyCategory name={category} list={DATA[category]} />
    ))}
  </>
);

const MyCategory = ({name, list}) => (
  <ul data-testid={name} role="list">
    {list.map(item => <li role="listitem">{item}</li>)}
  </ul>
);

Then let's say if we want to assert the list of movies that this MyContainer renders and in the same order as it's been rendered we can:

...
import { render, within } from "@testing-library/react";
import MyContainer from "../MyContainer";

describe("MyContainer", () => {
  it("tests movies", () => {
    const { getByTestId, getAllByRole } = render(<MyContainer />);

    const moviesCategory = getByTestId("movies");
    const movies = within(moviesCategory).getAllByRole("listitem");

    expect(items.length).toBe(2);
    expect(items[0]).toHaveTextContent("The Godfather");
    expect(items[1]).toHaveTextContent("Pulp Fiction");
  });
});

Real code is way more complex than this example, so this within helper function turns to be very convenient.

Apollo-React hooks can easily refetch queries

React-apollo hooks allow you to easily refetch queries after a mutation, which is useful for updating a list when you have create/update/delete mutations.

useMutation takes an argument called refetchQueries that will run queries named in the array.

import React from "react";
import { useMutation } from "@apollo/react-hooks";
import gql from "graphql-tag";
import { Button } from "react-native";
import { Item } from "./types";

const ITEM_DELETE = gql`
  mutation ItemDelete($id: ID!) {
    itemDelete(id: $id) {
      id
    }
  }
`;

interface Props {
  item: Item;
}

const DeleteItemButton = ({item}: Props) => {
  const [deleteItem] = useMutation(ITEM_DELETE, {
    variables: { id: item.id },
    refetchQueries: ["GetItemList"],
  });

  return (
    <Button title="Delete" onPress={() => deleteItem()} />
  )
}

Custom React Hook Must Use `use`

You can build your own hooks by composing existing hooks.

Here, I create a custom hook useBoolean by wrapping useState:

const useBoolean = () => useState(true);

Which I can then use in my component:

function Value() {
  const [value, setValue] = useBoolean();

  return <div onClick={() => setValue(!value)}>Click me {String(value)}</div>;
}

The react documentation very politely asks that you start the name of your hook with use. This is isn't strictly necessary, and it will still work if you call it:

const doBoolean = () => useState(true);

But that violates the Rules of Hooks.

You can include an eslint plugin that will prevent you from breaking the rules. This plugin is installed by default in create-react-app version 3.

How to write a render prop

Hi my name is Matt. This is how to write a dependecy-inverted React component using the render prop pattern. It's useful when you want to encapsulate and share common logic without knowing how it will be used or what children it should render. The render prop pattern is the successor to higher-order components and solves HoC's problems with naming collisions.

If you're on the latest version of React (>= 16.8) you should probably use a custom hook instead.

function FullName({ children, firstName, lastName }) {
	const fullName = firstName + ' ' + lastName
	return children(fullName)
}

// Usage:
function App() {
  return (
  	<FullName firstName="Thor" lastName="Odinson">
    	{fullName => <h1>Hello {fullName}!</h1>}
    </FullName>
  )
}

Simulate componentDidMount with a useEffect

If you want to simulate a componentDidMount class method inside your functional component, you can use the useEffect hook thusly:

const MyFunctionalComponent = () => {
   useEffect(() => {
   
     // any code here is run once 
     
   }, []); 
   
   // the empty array will be the same each 
   // time the component is called, so the useEffect
   // will not run in calls subsequent to the first
   
   return <div>Foo</div>;
}

ht: @greis

Don't async await, especially in useEffect

Often I need to setState based on an async value. With hooks this is something like:

useEffect(async () => {
    const newVal = await asyncCall();
    setVal(newVal);
});

But wait!. This throws an error. React wants the return of useEffect to be a cleanup function.

The return type of an async function is Promise. So that won't work. Best to just put on your big developer pants and use that promise.

useEffect(() => {
    asyncCall().then(setVal);
})

There. Now our useEffect returns undefined, and React is pleased.

Here is a sandbox if you want to see for yourself.

Set the relative path of assets in a CRA app

When I build my CRA app I get a path for my assets (css, images) that begins with /static. If I deploy my app to https://something.com/myapp, then the app will try to access those asset paths at https://something.com/static/asset.css. That's not where the asset lives. The asset lives at https://something.com/myapp/static/asset.css.

Create React App allows you to change the prefix for a the built assets with the homepage attribute in your package.json file.

You could set it to myapp:

"homepage": "/myapp"

And then the asset will have the path of /myapp/static/asset.css, but what if you want to change paths?

"homepage": "."

Setting homepage to . will make the asset always relative to index.html, allowing you to not be concerned with the path the application is deployed to.

This actually repurposes a property of the package json file that npm uses to set the homepage of an npm package, so you may find this property used in a different way in other package.json files.

See the npm docs here.

See the Create React App docs here

Override Create React App conf w/react-app-rewired

A common problem when using Create React App is changing the configured behaviour of webpack. Generally, if you want to change the webpack configuration provided by Create React App you need to eject, but eject at the very minimum adds a lot of files to your project that you may not want.

An alternative is to use react-app-rewired in combination with customize-cra.

react-app-rewired provides a file, config-overrides.js placed in your project root directory where you can override existing behaviour.

customize-cra provides a set of handy utility functions to help you override specific configurations.

For example, when using the ant design library, you can import both the component and the css for that component with one import line if you use babel-loader.

Here is an example of a config-overrides.js file that would provide that behaviour.

const { override, fixBabelImports } = require('customize-cra');

module.exports = override(
  fixBabelImports('import', {
    libraryName: 'antd',
    libraryDirectory: 'es',
    style: 'css',
  })
);

Run side effect when a prop changes w/Hooks

There is a React Hook for side effects useEffect. You can pass useEffect a function and that function will run after each render.

useEffect(() => console.log('rendered!'));

In many cases it's inefficient and unnecessary to call the effect function after every render. useEffect has a second argument of an array of values. If passing in this second argument, the effect function will only run when the values change.

useEffect(() => console.log('value changed!'), [props.isOpen]);

Now, you will see "value changed!" both on the first render and everytime isOpen changes.

Reminder: React Hooks are for functional components not class components. Check out the hooks api here

Get a ref to a dom element with react hooks

React Hooks are now available in React 16.8. There are 10 different hooks, you can read about them here. When I needed a ref to a dom element yesterday I reached for useRef.

const containerRef = useRef(null);

This isn't a ref to anything unless you pass the ref to a tag.

return (<div ref={containerRef}></div>);

Now the ref will be assigned a dom element that you can use. In this example I'm using the useEffect hook to execute a side effect after the render takes place. Use the current attribute to access the current dom node.

useEffect(() => {
	containerRef.current.style = 'background-color: green;'
})

Define Your Refs with React ElementRef Flow Type

If you're using flow in your React Native project, chances are that you are probably using refs. In this case, you'll need to define your ref types.

Let's say we have ref on a FlatList component from React Native:

import React from 'react'
import { FlatList } from 'react-native'
import data from './data.json'

class MyList extends React.Component {
  renderItem = () => {
    // rendering stuff
  }
  render() {
    return(
      <FlatList
        ref={l => (this.l = l)}
        data={data}
        renderItem={this.renderItem}
      />)
  }
}

Instead of being lazy and just using any, we'll use the React.ElementRef flow type. It takes an additional typeof argument, which is the component type of your ref.

// @flow
import * as React from 'react'
import { FlatList } from 'react-native'
import data from './data.json'

class MyList extends React.Component<{}, {}> {
  l: ?React.ElementRef<typeof FlatList>
  renderItem = () => {
    // rendering stuff
  }
  render() {
    return(
      <FlatList
        ref={l => (this.l = l)}
        data={data}
        renderItem={this.renderItem}
      />)
  }
}

Node Version - 10.14.1 Flow-Bin Version - 0.107.0

Flow docs - ref functions

Edited: 9/11/2019 - Fix fat arrow, Add typing to MyList, Update description to React Native, Resolve syntax error for ref typing

Navigate With State Via @reach/router

With @reach/router, you can programmatically change your route using the navigate function. This utilizes the Context API, so its available anywhere nested under your router. To provide some data to the destination location, include a state option in the navigate call.

const onSubmit = ({ data }) => {
  /* submit logic ... */

  navigate(nextPath, { state: { data }});
}

The component that renders in response to this navigation will have access to this state.

const NextComponent = ({ location }) => {
  const { data } = location.state;

  return (
    /* ... */
  )
}

Prevent reach/router Redirect Error Screen In Dev

When using @reach/router's <Redirect> with tools like create-react-app and Gatsby, you'll get those tools' development-mode error screen overlays whenever a redirect happens. This has to do with how @reach/router utilizes componentDidCatch to change the path without a render. That error screen overlay can get annoying though. Prevent it with the noThrow prop.

return (
  <Redirect to={anotherPath} noThrow />
);

React Fragments

Ordinarily React requires that each component returns a single child component. Sibling components cannot be returned in the render function:

// Valid
const MyComponent = () => (
  <div>
    <span>Chicken</span>
    <span>and</span>
    <span>Farm</span>
  </div>
);

// Invalid
const MyComponent = () => (
  <span>Chicken</span>
  <span>and</span>
  <span>Farm</span>
);

Sometimes, however, you have no need for the wrapper component and want to just render the siblings. React allows you to do this with the Fragment component, which satisfies the single child requirement without actually rendering a div in the DOM:

import {Fragment} from "react";

// Valid
const MyComponent = () => (
  <Fragment>
    <span>Chicken</span>
    <span>and</span>
    <span>Farm</span>
  </Fragment>
);

// Renders =>
<span>Chicken</span>
<span>and</span>
<span>Farm</span>

Read more about Fragments here: https://reactjs.org/docs/fragments.html

Check The Type Of A Child Component

There is a simple way to check the type of a child component.

import React from 'react';

const Hello = () => <h1>Hello</h1>;

const Greeting = ({ children }) => {
  let hello;
  React.Children.forEach(children, child => {
    if(child.type === Hello) {
      hello = child;
    }
  });

  return hello;
};

This is a comparison of the child's type to the component constant we are looking for.

This comparison is not the most robust. For instance, Gatsby does something internally that throws off this comparison. Here is a more robust comparison.

if(child.type === Hello || child.type === <Hello />.type)

source

Wrap The Root Of A Gatsby App In A Component

Each component that is defined in the pages directory of a Gatsby app will be generated into a separate static page. Each of these pages is meant to stand on its own. Nevertheless, there is still a behind-the-scenes root component above all of these pages. There are cases where'd you like to wrap this root component with some other component, such as a Redux Provider.

This can be done using the wrapRootElement hook from the Browser API in the gatsby-browser.js file.

// gatsby-browser.js
import React from 'react';
import { Provider } from 'react-redux';

import store from './src/store';

export const wrapRootElement = ({ element }) => {
  return (
    <Provider store={store}>{element}</Provider>
  );
}

Each page and each component in your Gatsby app will now be downstream from a Redux provider meaning that they can connect to the Redux store as needed. You can use this technique for any top-level component that need to be wrapped around the entire app.

source

Gatsby 404 Static Sitemap

Iterating through a process of building many static Gatsby.js pages at once, I discovered a hacky way to see what pages have been built. Visit a page that doesn't exist, and Gatsy's development 404 page will provide a sitemap with links to all the static pages.

demo

It's a little nicer than digging through the filesystem.

@reach/router Renders To A Div

Check out the following snippet that uses @reach/router.

import { Router } from '@reach/router';

const Home = () => <h1>Home</h1>;

const App = () => {
  return (
    <div className="main">
      <Router>
        <Home path="/home" />
      </Router>
    </div>
  );
}

When you visit '/home', this will render in the DOM as:

<div class="main">
  <div tabindex="-1" role="group" style="outline: none;">
    <h1>Home<h1>
  </div>
</div>

Notice the extra div -- that is what <Router> renders to as part of @reach/router's accessibility features. This may throw off the structure or styling of your app. This can be fixed. Any props that you give to <Router> will be passed down to that div. For instance, you could remove the most outer div and put className="main" on the <Router>.

source

Accessing Location Within @reach/router

The API of @reach/router departs a bit from react-router in a couple ways. The location prop which you may be used to having access to automatically is instead available through the Location component.

import React from 'react';
import { Location } from '@reach/router';

const MyComponent = () => {
  return (
    <Location>
      {({ location }) => {
        return <p>Current Location: {location.pathname}</p>;
      }}
    </Location>
  );
}

This is a contrived example, but you can imagine how you'd use it to access state or even create an HOC similar to withRouter.

Formik's Validation Schema As A Function

The most straightforward way to use Formik's validationSchema is to provide it with a Yup object defining your form's validations.

const MyComponent = withFormik({
  // ...

  validationSchema: yup.object().shape({
    email: yup.string().required(),
    feedback: yup.string().required(),
  }),
  
  // ...
})(MyForm);

There may be a point at which you need access to the props being passed to MyComponent in order to construct the proper set of validations. Formik supports this by allowing validationSchema to be a function.

const MyComponent = withFormik({
  // ...

  validationSchema: (props) => {
    let emailSchema;
    if(props.allowAnonymous) {
      emailSchema = yup.string();
    } else {
      emailSchema = yup.string().required();
    }

    return yup.object().shape({
      email: emailSchema,
      feedback: yup.string().required(),
    });
  },
  
  // ...
})(MyForm);

When validationSchema is a function, its first argument is the set of props passed to that component.

Don't rerender if nothing changed in React 16.6.0!

React 16.6.0 came out today and React now provides a handy function to create a component that won't rerender if it doesn't get new props, React.memo.


const BlueComponent = () => {
  return <div>no props don't rerender</div>;
}

const MemoComponent = React.memo(BlueComponent);

BlueComponent is a component that will re-render every time it's parent re-renders. It doesn't take props though, so it won't look any different based on new props. MemoComponent is a component created by passing BlueComponent to React.memo. It will not re-render when it's parent re-renders.

Check out another example in the code sandbox below.

Edit znw4wjn914

Read more about React 16.6.0 here.

Catching errors in React (16 and up)

If an error is thrown while rendering React, React unmounts the entire tree.

In production, this might not be behaviour you want. The behaviour might be inconsequential to the user's current path and why stop the user cold due to an unanticipated state?

React provides a function componentDidCatch to help manage exceptions and keep the consequence of the error localized to a specific part of your component tree.

This blog post describes the concept of an ErrorBoundary which can look like this:

class ErrorBoundary extends Component {
  componentDidCatch(error, {componentStack}) {
    console.log("error", error)
    console.log("componentStack", componentStack)
  }

  render() {
    return this.props.children;
  }  
}

Using the componentDidCatch lifecycle function this component will catch any error thrown by its children. If an error is thrown it will not render and none of it's children will render, but all components in different sections of the component tree will render.

The second argumunent to componentDidCatch is an object containing a key called componentStack which is a nice stack provided by React.

H/T Josh Branchaud

Return value of snapshot in componentDidUpdate

React has a rarely used lifecycle method getSnapshotBeforeUpdate where you get the opportunity to look at your current DOM right before it changes.

The return value of this method is the third parameter of componentDidUpdate.

getSnapshotBeforeUpdate(prevProps, prevState) {
	return "value from snapshot";
}

componentDidUpdate(prevProps, prevState, snapshotValue) {
  console.log(snapshotValue);
}

The above code will output value from snapshot in the log.

The purpose for getSnapshotBeforeUpdate given in the React documentation is for making sure a value that can't be set with rendering - like the scroll position - is exactly where you want it based on the state it was before the rerender.

My code example is here.

CSS !important Is Not Supported By Inline Styles

You can get pretty far with React's inline styling of components. There are however a few limitations. One such limitation is that the !important clause is not supported.

If you try applying !important to an inline style like so:

<div style={{ color: "red !important" }}>
  My div
</div>

You'll be disappointed when you open up the browser and inspect that div tag. The color rule will be ignored and excluded from the output html.

source

CRA Dependencies and ES5

At the time of this writing, create-react-app requires that all dependencies are precompiled to ES5. If they aren't, the build will abort.

When this happened to me today, I followed a few of the recommended workarounds, eventually switching to a library that uses the older ECMA standard.

Running $ yarn build-js (or equivalent) when testing out any dependency would be a good way to surface this incompatibility upfront.

Here's the Github issue on zipcodes-perogi where I documented my journey to this discovery.

Be specific in styled components with `&`

input[type="number"] has styles applied at a global level. I want to override some of those styles

a more specific selector takes precedence over a less specific one

So if I just define some css that is:

.myCoolInput {
  background-color: green;
}

It won't override the more specific input[type="number"] selector's background-color: pink.

So this styled component I have is wrong.

const GreenInput = styled.input`
	background-color: green;
`

render (
  <GreenInput type=["number"]/>
);

I need to use the & and move the className attribute to get more specificity for my style.

const GreenInput = styled(NumberInput)`
  input& {
	  background-color: green;
  }
`

This outputs:

input.<GreenInputClassId> {
  background-color: green;
}

This selector has the same specificity as input[type="number"] but is declared later, so takes precedence.

Don't forget the `className` prop!!

So this is normal styled-component stuff:

const RedDiv = styled.div`
  color: red;
`

which outputs

<div class='<someClassId>'>
</div>

.someClassId { color: red; } 

And then you can wrap that div

const BlueDiv = styled(RedDiv)`
  color: blue;
`

which outputs:

<div class='<someClassId>'>
</div>

.someClassId { color: blue; } 

And you can also style existing components. If I have the component:

const CoolComponent = (props) => {
  render(
    <div></div>;
  )
};

const PurpleDiv = styled(CoolComponent)`
  color: purple; 
`

That outputs

<div></div>

It's not purple!!

That's because if you want to style non-styled-component components with styled-components then you have to use the className prop to set the class onto your element.

const CoolComponent = ({className}) => {
  render(
    <div className={className}></div>;
  )
};

Which outputs

<div class='<someClassId>'>
</div>

.someClassId { color: purple; }