ReactJS Essentials

15 August 2019 9 min read
#reactjs#javascript

Contents


What is ReactJS?

React (a.k.a. ReactJS or React.js) is a JavaScript library for creating user interfaces, open sourced to the world by Facebook and Instagram team in 2013. One might think of it as the “View” in the “Model-View-Controller” pattern.

React’s main goal is to make development of UI components easy and modular. It is intended to ease the process of building large applications using data that changes over time.

React was created by Jordan Walke, a software engineer at Facebook, with the influence of XHP, a PHP-based component system that is still in use at Facebook, but also by functional programming ideas. Pete Hunt wanted to use React at Instagram, so he pushed to extract React from Facebook-specific code and open source it.

React has gained a lot of popularity for its concept of a “virtual-DOM,” which allows it to determine which parts of the DOM have changed by diffing the new version with the stored virtual DOM, and using the result to determine how to most efficiently update the browser’s DOM.

How the magic happens

To get an idea of what’s going on inside React, take a look at the following diagrams demonstrating how React.js rendering and the React Virtual DOM work:

f1

FIGURE 1: REACT.JS RENDERING

f2

FIGURE 2: THE REACT VIRTUAL DOM

React basically batches DOM updates and then applies minimal diffs to the real DOM.

A simple React component

React components implement a render() method that takes input data and returns what to display.

Here is an example of a simple “Hello” Component:

class Hello extends React.Component {
  render() {
    return <div>Hello {this.props.name}</div>
  }
}
ReactDOM.render(<Hello name="World" />, document.body);

Component Specifications

NAME DESCRIPTION
render The render() function should be pure, meaning that it does not modify the component state. It should examine this.props and this.state and return a single child element.
getInitialState Invoked once before the component is mounted. The return value will be used as the initial value of this.state.
getDefaultProps Invoked once and cached when the class is created. Values in the mapping will be set on this.props.
propTypes The propTypes object allows you to validate props being passed to your components.
displayName The displayName string is used in debugging messages. JSX sets this value automatically.

Components API

NAME DESCRIPTION
setState Merges nextState with the current state.
replaceState Like setState(), but deletes any pre-existing state keys that are not in nextState.
forceUpdate Call render() on the component, skipping shouldComponentUpdate().
React.findDOMNode Returns the corresponding native browser DOM element.
isMounted Returns true if the component is rendered into the DOM.
setProps Changes the properties and triggers a rerender.
replaceProps Like setProps(), but deletes any preexisting props instead of merging the two objects.

Lifecycle methods

NAME DESCRIPTION
componentWillMount Invoked once, both on the client and server, immediately before the initial rendering occurs.
componentDidMount Invoked on the client immediately after the initial rendering occurs.
componentWillReceiveProps Invoked when a component is receiving new prop. Use setState() here.
shouldComponentUpdate Invoked before rendering when new props or state are being received. Skips render() if it returns false.
componentWillUpdate Invoked immediately before rendering when new props or state are being received. Can’t use setState() here.
componentDidUpdate Invoked immediately after the component's updates are flushed to the DOM. Operate on the DOM here.
componentWillUnmount Invoked immediately before a component is unmounted from the DOM.

States and properties

Props and states are both plain JS objects; a change within one of them will trigger a render. These objects are deterministic.

<VideoComponent fullscreen={true} />
// props
this.props.fullscreen //=> true

// state
this.setState({ user: 'hemanth' });
this.replaceState({ ... });
this.state.username //=> 'hemanth'
render() {
  return (
    <div className={this.props.fullscreen ? 'full' : ''}>
      Hello, {this.state.username}
    </div>
  )
}

Pre-populates states and props:

class Hello extends React.Component{
  getInitialState() {
    return { comments: [] }
  },
  getDefaultProps() {
    return { name: "Hello" }
  }
}

Deciding when to use props and when to use state might get tricky. The following table will help to simplify this decision:

DECIDING FACTOR PROPS STATE
Can get initial value from parent Component? Yes Yes
Can be changed by parent Component? Yes No
Can set default values inside Component? Yes Yes
Can change inside Component? No Yes
Can set initial value for child Components? Yes Yes
Can change in child Components? Yes No

AJAX Requests

React by default doesn't provide a helper method to manage AJAX requests, but you can use any other third party JavaScript library—like jQuery or Zepto—to make necessary AJAX requests.

Below is a sample code snippet that performs an AJAX request on props.url and on success sets the data state. In case of an error, it just uses console.error to report the error.

NOTE: Make sure that the execution context (this) is bound to the success and error callbacks.

componentDidMount() {
  $.ajax({
    url: this.props.url,
    dataType: 'json',
    cache: false,
    success: (data) => {
      this.setState({ data })
    },
    error: (xhr, status, err) => {
      console.error(this.props.url, status, err.toString())
    }
  })
}

Styling Your Components

In React, styles are mentioned in line, but unlike the traditional way of inline CSS strings, here we specify each style as an object whose key is the camelCased version of the style name, and whose value is the style’s value (usually a string).

const divStyle = {
  color: 'white',
  backgroundImage: `url(${imgUrl})`,
  WebkitTransition: 'all', // note the capital 'W' here
  msTransition: 'all'
  // 'ms' is the only lowercase vendor prefix
}

ReactDOM.render(
  <div style={divStyle}>Hello World!</div>,
  mountNode
)

Most numeric values at the end of a style prop receive an automatic “px” specification added to them (e.g., “width: 10” is read as “width: 10px”). Here is a list of properties that won’t get the automatic “px” suffix:

  • boxFlex
  • boxFlexGroup
  • columnCount
  • fillOpacity
  • flex
  • flexGrow
  • flexPositive
  • flexShrink
  • flexNegative
  • fontWeight
  • lineClamp
  • lineHeight
  • opacity
  • order
  • orphans
  • strokeOpacity
  • widows
  • zIndex
  • zoom

DOM helpers

References: Help to access the DOM nodes:

<input ref="firstName">
this.refs.firstName
React.findDOMNode(this.refs.firstName).focus()
React.findDOMNode(this.refs.firstName).value

DOM Events: Help to handle DOM events:

<input type="text"
  value={this.state.value}
  onChange={this.handleChange}
/>

handleChange: (event) => {
  this.setState({ value: event.target.value })
}

Validating properties:

NAME DESCRIPTION
Primative Types .string .number .func .bool
Reactive Elements .element .node
Enumerables .oneOf .oneOfType
Arrays and Objects .array[Of] .object[Of] .instanceOf .shape

Sample usage:

propTypes: {
  email: propTypes.string,
  firstName: propTypes.string,
  age: propTypes.number,
  gender: propTypes.oneOf(['M','F','NA'])
  node: propTypes.node,
  cb: propTypes.func.isRequired,
}

Custom validation:

propTypes: {
  customProp: (props, propName, componentName) => {
    if (!/matchme/.test(props[propName])) {
      return new Error('Validation failed!')
    }
  }
}

conventions for react DOM & JSX

A few patterns and best practices to get you started:

  • React DOM elements are expected to be in camelCase.
  • The camelCasing of DOM matches what you would write for custom components: <Typeahead onClick=../> and <div onClick=../>
  • These camelCased attributes are usually what you write when updating the DOM via JS (input.maxLength).
  • One current confusing exception is that class= is normalized automatically into className= at transform time.
  • Use the className attribute instead of class
  • Use the htmlFor attribute instead of for
  • Use <textarea value="something"> instead of <textarea>something</textarea>
  • Custom HTML attributes may be data-attr and all lower case.
  • Make use of ternary operators, wherever required. For example: React.render(<div id={condition ? ‘msg’ : ‘’}>Hello World!</div>, mountNode)

ANTI-PATTERN

Using props in getInitialState is an anti-pattern. Instead of:

class MessageBox extends React.Component{
  getInitialState() {
    return { nameWithQualifier: `Mr. ${this.props.name}` }
  }
  render() {
    return <div>{this.state.nameWithQualifier}</div>
  }
}
ReactDOM.render(<MessageBox name="Rogers"/>, mountNode)

Try the following:

class MessageBox extends React.Component{
  render() {
    return <div>{`Mr. ${this.props.name}`}</div>
  }
}
ReactDOM.render(<MessageBox name="Rogers"/>, mountNode)

NOTE: This is not an anti-pattern if we make it clear that synchronization is not the goal.

SPREAD OPERATOR IN JSX

const props = { foo: x, bar: y }
const component = <Component {...props} />

You can also use the spread operator (...) to override props:

const props = { foo: 'default' };
const component = <Component {...props} foo={'override'} />

Flux: the application architecture

Flux plays a key role if your application uses dynamic data.

Don’t try to compare Flux to Model-View-Controller (MVC) architecture. Flux is just a term to describe smart, unidirectional data flow.

f3

FIGURE 3: AN OVERVIEW OF FLUX ARCHITECTURE

Keys ideas in the Flux architecture

  1. Views “Dispatch” “Actions”.
  2. “Store” Responds to Dispatched Events.
  3. Store Emits a “Change” Event.
  4. View Responds to the “Change” Event

Points to remember

  1. A “dispatcher” is essentially an event system.
  2. There is at most one global dispatcher.
  3. “Store” is specific collection of logic and data.
  4. A “Store” is a singleton.
  5. A store is not a model. A store contains models.
  6. A store is the only entity in the application that is aware of how to update data.
  7. Only stores registers to dispatcher callbacks. store emits an event, but not using the dispatcher!
  8. When store data changes, your views shouldn’t care if things were added, deleted, or modified but just re-render.

Testing your application with jest

Jest allows for painless JavaScript Unit Testing. It is built on top of the Jasmine test framework.

Consider a scenario where you want to test the following file:

// div.js
const div = (value1, value2) => {
  return value1 / value2
}

export default div
  1. Create a directory __tests__/ with a file div-test.js
import div from '../div'

describe('div', function() {
  it('divides 4 / 2 to equal 2', () => {
    expect(div(4, 2)).toBe(2)
  })
})
  1. Run npm install jest-cli --save-dev
  2. Add the following to your package.json
  { "scripts": { "test": "jest" } }
  1. Run npm test
  [PASS] __tests__/div-test.js (0.015s)


SOURCES

HEMANTH H.M. React Essentials on DZone

GO FURTHER