Skip to content

Latest commit

 

History

History
344 lines (263 loc) · 11.8 KB

react-notes.md

File metadata and controls

344 lines (263 loc) · 11.8 KB

JSX

JSX is a shortcut for using the React.createElement() API, that is much more concise, easy to read, and visually looks a little like the generated UI (as both are tree-like).

JSX is tag-based like XML. Each tag, like <div />, is transformed into a call to React.createElement(). Any attributes become props of the instantiated component.

// JSX
const a = <div />

const b = (
  <div
    foo='hello'
    bar={baz}>
    <span>42</span>
  </div>
)

// Javascript equivalent
var a = React.createElement('div', null);

var b = React.createElement(
  'div',
  {
    foo: 'hello',
    bar: baz },
  React.createElement(
    'span',
    null,
    '42'
  )
);

There are two libraries we'll use:

  • react contains the APIs for creating components
  • react-dom contains the APIs for rendering to the browser DOM

React Updating Cycles and class vs function

  • For simple React components, we can use a function. For example:

     import React from 'react'
     import { render } from 'react-dom'
     
     const node = document.querySelector('#app')
     const element = <div>Hello World!</div>
     
     render(element, node)
  • Often we want to render more complex components which encapsulate logic, styles, and multiple DOM components, and use lifecycle events (e.g. componentWillMount) and set any states. In this case, we want to use should use class.

     import React, { Component } from 'react'
     import { render } from 'react-dom'
    
     class Counter extends Component {
     
       state = {count: 0}
     
       componentDidMount() {
         setInterval(() => {
           this.setState({count: this.state.count + 1})
         }, 1000)
       }
     
       render() {
         const {count} = this.state
         const {color, size} = this.props
     
         return (
           <div style={{color, fontSize: size}}>
             {count}
           </div>
         )
       }
     }
     
     class App extends Component {
       render() {
         const style = {
           flex: 1,
           justifyContent: 'center',
           alignItems: 'center',
         }
     
         return (
           <div style={style}>
             <Counter color={'lightblue'} size={16} />
             <Counter color={'skyblue'} size={32} />
             <Counter color={'steelblue'} size={80} />
             <Counter color={'darkblue'} size={140} />
           </div>
         )
       }
     }
     
     render(<App />, document.querySelector('#app'))
  • If you use class, make sure to call super() if you have a constructor.

ES6 class constructors MUST call super() if they are subclasses. Thus, you have to call super() as long as you have a constructor. (But a subclass does not have to have a constructor)

import React, { Component } from 'react'
import { render } from 'react-dom'

// OK
class MyClass extends Component {
	render(){
		return <div>Hello { this.props.world }</div>;
    }
}

// OK
class MyClass extends React.component{
	constructor(props){
		super(props);
		console.log(this.props); // this.props is undefined
    }
}

// OK
class MyClass extends React.component{
    render(){
    	// There is no need to call `super(props)` or even having a constructor 
    	// this.props is automatically set for you by React 
    	// not just in render but another where else other than the constructor
        console.log(this.props);  // it works!

    }
}

In summary:

  • Calling super() is necessary if you need to have a constructor.
  • Calling super(props) is necessary if you want to access this.props inside the constructor
  • ES6 class constructors MUST call super() if they are subclasses; however a subclass does not have to have a constructor.

According to the Official Documentation

The constructor for a React component is called before it is mounted. When implementing the constructor for a React.Component subclass, you should call super(props) before any other statement. Otherwise, this.props will be undefined in the constructor

The constructor is the right place to initialize state. To do so, just assign an object to this.state; don’t try to call setState() from the constructor. The constructor is also often used to bind event handlers to the class instance.

If you don’t initialize state and you don’t bind methods, you don’t need to implement a constructor for your React component.

Note, The difference between constructor and getInitialState is the difference between ES6 and ES5 itself. getInitialState is used with React.createClass and constructor is used with React.Component.

import React, { Component } from 'react'
import { render } from 'react-dom'
class MyClass extends React.component{
	constructor(props) {
		super(props);
	  	this.handleChange = this.handleChange.bind(this);
	  	this.state = {
	  		color: props.initialColor
	  	}
	}
	handleChange(e) {
		this.setState({temperature: e.target.value});
	}
	render() {
		// ...
	}
	
}

Beware of this pattern, as state won’t be up-to-date with any props update. Instead of syncing props to state, you often want to lift the state up instead.

Often, several components need to reflect the same changing data. We recommend lifting the shared state up to their closest common ancestor. Let’s see how this works in action.

TL;DR You want to make children render this.props instead of this.state and have the parent pass a function (say changeParentProps which modifies its states, to the chilren so the children can call onClick={this.props.changeParentProps} in their render functions.

Styling React Component - Three ways:

  1. Inline Styles
    • Pro: No libraries/dependencies), dynamic (variables, themes, merging, etc) and works on Works on every React renderer (web, native, etc)
    • Con: No CSS features (psuedoclasses, media queries, keyframe animations) out of the box.
  2. CSS and Class Names
    • Simply write CSS using any pre-processor or post-processor you'd like (LESS, Sass, Stylus, PostCSS), and use the className attribute of your components to apply styles. With this option, you can have many of the CSS features for dynamic styles - pseudo classes (:hover), media queries, keyframe animations, etc.

Event Handling

// Bad practice

import React, { Component } from 'react'
import { render } from 'react-dom'

class CounterButton extends Component {

  state = {count: 0}

  render() {
    const {count} = this.state

    return (
      <button type='button' onClick={() => this.setState({count: count + 1})}>
        Click HERE to increment: {count}
      </button>
    )
  }
}

render(<CounterButton />, document.querySelector('#app'))

It's generally bad practice to define functions within the props of your React elements like we did in the previous example. This is because a new function will be created each time render is called - it's common for components to compare props using ===, which in this case will indicate that the onClick prop of the button has changed, and may cause unnecessary re-renders. Using .bind within component props has a similar effect.

// Better

import React, { Component } from 'react'
import { render } from 'react-dom'

class CounterButton extends Component {

  state = {count: 0}

  handleClick = () => {
    const {count} = this.state

    this.setState({count: count + 1})
  }

  render() {
    const {count} = this.state

    return (
      <button type='button' onClick={this.handleClick}>
        Click HERE to increment: {count}
      </button>
    )
  }
}

render(<CounterButton />, document.querySelector('#app'))

Conditional Rendering

// Rendering with &&
{subtitle && (
	<h2 style={styles.subtitle}>{subtitle}</h2>
 )}

// Rendering with ternary ?

{subtitle ? (
	<h2 style={styles.subtitle}>{subtitle}</h2>
	) : (
	<h3 style={styles.empty}>No subtitle</h3>
)}

// Rendering with if/else
class Card extends Component {
  renderContent() {
    const {title, subtitle} = this.props

    return (
      <div>
        <h1 style={styles.title}>{title}</h1>
        {subtitle ? (
          <h2 style={styles.subtitle}>{subtitle}</h2>
        ) : (
          <h3 style={styles.empty}>No subtitle</h3>
        )}
      </div>
    )
  } 
  render() {
    const {loading, error} = this.props

    let content

    if (error) {
      content = 'Error'
    } else if (loading) {
      content = (
        <h3 style={styles.empty}>Loading...</h3>
      )
    } else {
      content = this.renderContent()
    }

    return (
      <div style={styles.card}>
        {content}
      </div>
    )
  }
}

class App extends Component {
  render() {
    return (
      <div>
        <Card loading={true} />
        <Card error={true} />
        <Card title={'Title'} subtitle={'Subtitle'} />
      </div>
    )
  }
}

List and Keys

  • Every React component can be passed a special prop called the key. React uses this key to determine the rendered element's identity. Understanding element identity is critical for performance and minimizing DOM manipulation: for example, if the first element in a list of thousands should no longer be rendered, React needs some way to detect this. Instead of re-rendering thousands of elements, React can simply remove a single DOM node for the first element, which is much more efficient.
  • if you wanted to, you could assign a key to every element. You can even force a component to re-render by assigning a different key (this tells React that the element's identity has changed, thus triggering a re-render). But most of the time, you don't need to consider keys because React takes care of them automatically. It's likely the only time you'll need to use them is when rendering lists of elements.
  • Ideally, we want to make the key the unique identifier for the items in the list (i.e., its actual value). If items in our data have no unique identifier of any kind, then we will generally resort to using the index of the item as its key. This silences the warning from React about forgetting to include keys, but remember that doing this will cause React to identify elements incorrectly if the data is modified

Learning Resources

React

React Router

  1. Read this for more on how to fix the deprecation warnings.

    Failed Context Types: Calling PropTypes validators directly is not supported by the prop-types package