React

React is a Javascript framework for implementing dynamic single page applications into your site.

Setting Up

Use create-react-app with npm or yarn, or build it from scratch.

# Using npm
npx create-react-app my-app
# Using yarn
yarn create react-app my-app

Components

Components are reusable pieces of JSX that can be used to dynamically render HTML. The JSX is written as is, outside of any quotes. When components are referenced, they are written with the closing /. Components are named in PascalCase.

Note the parentheses used in the arrow function instead of curly braces. This is because the implicit return will enclose everything within the parentheses and allow it to be placed on multiple lines.

const FoodItem = () => (
  <li>Get some of this food!</li>
);

const GroceryList = () => (
  <ul>
    <FoodItem />
    <FoodItem />
  </ul>
);

These are rendered via the render method of the ReactDOM, which takes the component followed by the destination it will be appended to. Most common is that you will have a single div that your whole website will be injected into, i.e. <div class="app"></div>.

ReactDOM.render(<GroceryList />, document.getElementById('main'));
<!-- output in associated HTML file -->

<div id="main">
  <ul>
    <li>Get some of this food!</li>
    <li>Get some of this food!</li>
  </ul>
</div>

Component Properties, or Props

Properties are attributes that can be passed into and used to populate parts of that component. Props are always passed in as an object of keys that correspond to the attributes given on the JSX element. e.g. <Component name={"John"} age={6} /> will pass { name: "John", age: 6 } into the Component. Props are surrounded by curly braces when they are assigned and when they are recalled. Inside the curly braces, valid Javascript may be called.

const GroceryListItem = (props) => (
  <li>{props.item.toUpperCase()}</li>
);

const GroceryList = (props) => (
  <ul>
    {props.groceryItems.map(propItem => <GroceryListItem item={propItem} />)}
  </ul>
);

ReactDOM.render(
  <GroceryList groceryItems={['candy', 'cookies', 'ice cream']} />,
  document.getElementById('app')
);
<!-- output in associated HTML file -->

<div id="app">
  <ul>
    <li>CANDY</li>
    <li>COOKIES</li>
    <li>ICE CREAM</li>
  </ul>
</div>

Default Props

const GroceryListItem = (props) => (
  <li>{props.item.toUpperCase()}</li>
);

GroceryListItem.defaultProps = {
  item: 'Milk',
};

const GroceryList = (props) => (
  <ul>
    <GroceryListItem />
  </ul>
);

ReactDOM.render(<GroceryList />, document.getElementById('app'));
<!-- output in associated HTML -->

<div id="app">
  <ul>
    <li>MILK</li>
  </ul>
</div>

PropTypes

You can add type checking to your props to make your code a bit more robust. If the types you entered don't match the element's type, then it will only give a warning in the console and will still render (as long as it is able). Learn more here.

Exporting Components

You can put the component into another file and import it in by using the export default componentName syntax.

// GroceryListItem.js
const GroceryListItem = (props) => (
  <li>{props.item.toUpperCase()}</li>
);

export default GroceryListItem;
// app.js
import ...
import GroceryListItem from './GroceryListItem.js';

ReactDOM.render(
  <GroceryListItem item="candy" />,
  document.getElementById('app')
);

Higher Order Components

Higher order components are just higher order functions in React, in that they are functions that spit out another function based on what is input. One example is a React HOC that takes in a component and adds logging capabilities to it.

TrackingWrapper.jsx

import ...

const TrackingWrapper = ({ WrappedComponent, props }) => {
  const logInteraction = (event) => {
    const logObject = {
      timstamp: Date.now(),
      element: event.target,
    };
    console.log(logObject);
    // POST to server
  }

  return (
    <div className="tracking-wrapper" onClick={this.logInteraction}>
      {/* This will pass in all props passed to the WrappedComponent in their given namespace */}
      <WrappedComponent {...props} />
    </div>
  );
};

export default TrackingWrapper;

App.jsx

import ...
import TrackingWrapper from './TrackingWrapper';
import { ClickMe } from './Buttons';

const App = () => {
  return (
    <TrackingWrapper
      WrappedComponent={ClickMe}
      {/* The following props will get passed to the ClickMe component */}
      message="Click here!"
      link="http://www.zombo.com"
    />
  );
};

export default App;

Another common use of higher order components is a wrapper that will enclose multiple child components, called children[9]. You could do this with the above example like so:

TrackingWrapper.jsx

import ...

const TrackingWrapper = ({ children }) => {
  const logInteraction = (event) => {
    const logObject = {
      timstamp: Date.now(),
      element: event.target,
    };
    console.log(logObject);
    // POST to server
  }

  return (
    <div className="tracking-wrapper" onClick={this.logInteraction}>
      {/* This will pass in all children components that this component encloses */}
      { children }
    </div>
  )
};

export default TrackingWrapper;

App.jsx

import ...
import TrackingWrapper from './TrackingWrapper';
import { ClickMe } from './Buttons';

const App = () => {
  return (
    <TrackingWrapper>
      <ClickMe
        message="Click here!"
        link="http://www.zombo.com"
      />
    </TrackingWrapper>
  );
};

export default App;

User Interaction/Event Listeners

Components can have local event listeners assigned directly to themselves.

const GroceryListItem = (props) => {
  const onListItemClick = function (e) {
    console.log("Yum!");
  };

  return (
    <li onClick={onListItemClick}>{props.item}</li>
  )
};

If you want to use an event listener on a class component that will be used within another this context, you need to be sure you maintain the correct this, either through bind, or the more sexy use of an anonymous arrow function.

// Good
return (
  <li onClick={this.onListItemClick.bind(this)}>{props.item}</li>
)

// Better
return (
  <li onClick={() => this.onListItemClick()}>{props.item}</li>
)

Stateless Functional vs. Class

All of the previous examples have used stateless functional components, which are declared at runtime and essentially become a static web page (unless you are using hooks, which make them no longer stateless). If you want to have user interaction and a dynamic webpage, you will want to include state. State is just a dynamic memory for a given component, e.g. whether it has been clicked or has a given property. State is only included on class components and is initialized in the constructor.

First you will want to convert your stateless functional component into a class component.

class GroceryListItem extends React.Component {
  constructor (props) {
    super(props);
  }

  render () {
    return (
      <li>{this.props.item}</li>
    )
  }
};

Then you can add your state to the element via the class. When this.setState is called, the element rerenders in the DOM, applying whatever changes you have made to the state and control flow of the rendering of the element.

class GroceryListItem extends React.Component {
  constructor (props) {
    super(props);

    this.state = {
      hover: false,
    };
  };

  onClick () {
    this.setState(
      { hover: !this.state.hover },
    );
  };

  render () {
    const style = {
      fontWeight: this.state.hover ? "bold" : "normal"
    };

    return (
      <li style={style} onClick={() => this.onClick()}>{this.props.item}</li>
    );
  };
};

For the element to properly rerender, you must update the state by using the setState method. This will cause what React calls "reconciliation", which will update what needs to be updated in the DOM.

setState

The setState call takes in two arguments: setState(updated, [callback]):

class GroceryListItem extends React.Component {
  constructor (props) {
    super(props);

    this.state = {
      hover: false,
    };
  };

  onClick () {
    this.setState(
      { hover: !this.state.hover },
      () => console.log(`Hover set to ${this.state.hover}.`)
    );
  };

  render () {
    const style = {
      fontWeight: this.state.hover ? "bold" : "normal"
    };

    return (
      <li style={style} onClick={() => this.onClick()}>{this.props.item}</li>
    );
  };
};

Handling Application-wide Constants[8]

If you have a lot of constants that need to be accessed in multiple files or components, create a constants file and bring in the file as an import (this also helps with magic numbers).

Before

components/ExampleComponent.js

import React from 'react';

const STATE = 'CA';
const TAX_RATE = .095;

const ExampleComponent = (bill) => (
  <div>
    <h1>Total Tax in {STATE}: {bill + bill * TAX_RATE}</h1>
  </div>
);

After

components/ExampleComponent.js

import React from 'react';

import constants from '../data/constants';

const ExampleComponent = (bill) => (
  <div>
    <h1>Total Tax in {constants.STATE}: {bill + bill * constants.TAX_RATE}</h1>
  </div>
);

data/constants.js

const STATE = 'CA';
const TAX_RATE = .095;

export default {
  STATE,
  TAX_RATE,
};

References

  1. https://reactjs.org/docs/introducing-jsx.html
  2. http://jamesknelson.com/javascript-return-parenthesis/
  3. https://stackfan.com/loop-through-an-array-of-objects-in-react/
  4. https://css-tricks.com/understanding-react-setstate/
  5. https://reactjs.org/docs/react-component.html#setstate
  6. https://reactjs.org/docs/typechecking-with-proptypes.html
  7. https://www.npmjs.com/package/prop-types
  8. https://medium.com/@austinpaley32/how-to-add-a-constants-file-to-your-react-project-6ce31c015774
  9. https://dev.to/franca/react-children-2k4e
  10. create-react-app
Incoming Links

Last modified: 202401040446