Today I Learned

A Hashrocket project

23 posts about #react

Accessing 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

glamorous composition for inline styling

glamorous is a library to help style components from the component itself rather than from a css stylesheet. You create a component with style in the form of a javascript object.

require glam import 'glamorours';

const InfoSpan = glam.span({
  backgroundColor: 'none',
  width: '20%',
  margin: '0 1rem',
  padding: '0 1rem',
  display: 'inline-block',
  boxSizing: 'borderBox'
});

Later when rendering, you can use this as if it were a component.

render() {
  return (
    <div>
      <InfoSpan>
        Some Important Information
      </InfoSpan>
    </div>
  );
}

You can also compose additional glamorous components by re-using previously declared glamorous components.

const LeftSpan = glamorous(InfoSpan, {
  textAlign: 'right'
});

const RedArticle = glamorous(Article, {
  textAlign: 'left'
});

Catchall in a React Router Switch

Today we built a 404 page for a React app using React Router, via a Switch statement with no specified path. Like a default clause, it catches if none of the other routes match:

<Switch>
  <Route path="/about" component={About}/>
  <Route component={NoMatch}/>
</Switch>

On any other path besides /about, we’ll render our NoMatch component.

Custom Browser with Create React App

Sometimes Google Chrome isn’t your default browser for surfing the internet, but it is your default browser during development. With create-react-app you can specify an alternate browser to automatically open (or no browser at all), to avoid opening the wrong development browser all the time.

Just pre-pend the browser to the start script:

{
  "name": "custom-browser",
  "version": "0.1.0",
  ...
  
  "scripts": {
    "start": "BROWSER='Google Chrome' react-scripts start",
    ...
  }
}

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.

Legacy Refs vs Ref Callbacks

Reactjs provides a way to get references to dom elements that react is rendering through jsx. Previously, it was through what are now legacy refs:

componentWillUpdate() {
  this.refs.thing.tagName == "div";
}

render() {
  return (
    <div ref="thing"/>
  );
}

Where you can assign an element an identifier and react would keep a refs hash up to date with references to the dom for that element.

The react docs say this about the previous system.

We advise against it because string refs have some issues, are considered legacy, and are likely to be removed in one of the future releases.

The new system uses callbacks

render() {
  return (
    <div ref={(div) => { console.log('tag name:', div.tagName); }} />
  );
}

This callback is called when the component mounts with a reference to the dom element as an argument. Importantly, when the component unmounts the callback is called again but this time with null as an argument.

.babelrc ignores babel settings in package.json

There are three ways to provide babel configuration listed on the usage page for babelrc. You can provide a .babelrc file, you can place a babel section in your package.json or you can declare configuration with an env option.

If you have both a .babelrc in your root dir AND a babel section in your package.json file then the settings in the package.json will be completely ignored.

This happened to me when needing to declare an extra plugin for babel that create-react-app did not provide. I ejected from create-react-app, I added a .babelrc that declared the extra plugin, and this broke the build for my app. The babel configuration for an ejected create-react-app is in the package.json file.

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.

Render An Array Of Elements With React 16

React 16 was released today. Among many exciting features and updates is support for rendering an array of elements.

This can look as simple as this example:

return [
  <li key="1">One</li>,
  <li key="2">Two</li>,
  <li key="3">Three</li>
];

It really shines in the case of generating elements from an array of data.

let data = [
  { value: "One", key: "1" },
  { value: "Two", key: "2" },
  { value: "Three", key: "3" }
];
return data.map(item => {
  return (
    <li key={item.key}>
      {item.value}
    </li>
  );
});

No need to wrap the result in a <div>!

source

Export a synchronous method in React Native module

If you are working with an existing brownfield iOS app you may already have some code that is really complicated to change and sometimes you want a bridge module to export some native methods to JS that return synchronous values right way. In iOS, you can use the macro RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD:

@define API_URL @"http://localhost:3000"

@implementation ConfigManager

RCT_EXPORT_MODULE();

RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getApiUrl) {
  return API_URL;
}

@end

And in your JS file you can call:

import { NativeModules } from 'react-native';

const apiUrl = NativeModules.ConfigManager.getApiUrl();

There is a problem using that when your app is in debug mode. It will raise the error global.nativeCallSyncHook is not a function. Also this macro blocks the current thread, so only use it if you REALLY need it.

Handle JS Errors in React 16

React 16 introduced a new method called componentDidCatch to catch all JS errors. It can catch errors during rendering, lifecycle methods and constructors of the whole tree below.

class App extends React.Component {

  componentDidCatch(error, info) {
    ErrorService.log(error, info);
  }

  render() {
     // code that raises error, it could come from this component or its children.
  }
}

Invisible components in React Native

Sometimes you want a component to be rendered but not visible to the user and in React Native 0.42 and below, you could accomplish that by setting the component’s height and width to 0.

The good news is that React Native 0.43 will make it easier and introduce the CSS property you are already familiar with: display: 'none'.

You can already try it installing the pre-release version 0.43.0-rc.4.

React wrapper components with nested children

Say you want to create a component that acts as a wrapper such that you can pass it nested child components like so:

<WrapperComponent title="I am the wrapper">
  <ChildComponent body="Hello from child component" />
</WrapperComponent>

This can be achieved by using this.props.children as in this example:

class WrapperComponent extends Component {
  render() {
    return (
      <div className="wrapper">
        <h1>{this.props.title}</h1>
        {this.props.children}
      </div>
    );
  }
}

class ChildComponent extends Component {
  render() {
    return (
      <p>{this.props.body}</p>
    );
  }
}

class App extends Component {
  render() {
    return (
      <WrapperComponent title="I am the wrapper">
        <ChildComponent body="Hello from child component" />
      </WrapperComponent>
    );
  }
}

Mounting App will produce the following markup:

<div class="wrapper">
  <h1>I am the wrapper</h1>
  <p>Hello from child component</p>
</div>

TouchableHighlight with custom component

Usually in React Native when you want to add touch events to a custom component you just wrap it with a Touchable* component.

If you want to use TouchableOpacity there is no need to change anything on the custom component, but if you want to use TouchableHighlight you have to change the component and implement setNativeProps and delegate to the container View:

class CustomComponent extends Component {
  render() {
    return (
      <View ref='container'>
        ...
      </View>
    )
  }

  setNativeProps(nativeProps) {
    this.refs.container.setNativeProps(nativeProps);
  }
}

And then you are ready to go:

<TouchableHighlight onPress={this._onPress} underlayColor="#fc0">
  <CustomComponent />
</TouchableHighlight>