Skip to content

Instantly share code, notes, and snippets.

@0bie
Last active February 23, 2024 23:32
Show Gist options
  • Save 0bie/a2876b2509e9d5deed7101ef796533d7 to your computer and use it in GitHub Desktop.
Save 0bie/a2876b2509e9d5deed7101ef796533d7 to your computer and use it in GitHub Desktop.
Beginner Notes on React JS

React Notes

JSX

  • JSX adds XML syntax to JS

  • JSX tags have a tag name, attributes, and children (similar to XML)

  • Similar to JS, quotes in JSX attributes represent strings. Numeric values and/or expressions should be wrapped in a curly brace.

  • E.g

             <div className =“red”>Children Text</div>;
             <myCounter count={3 + 5} />;
             var gameScores = {
                 player1  :   2,
                 player2  :   5
             } ;
             <DashboardUnit data-index=“2”>
                 <h1>Scores</h1>
                 <Scoreboard className=“results” scores={gameScores} />
             </DashboardUnit>;
    
  • <myCounter> has an attribute called count that takes a numeric expression as its value

  • gameScores is an object literal that has two prop-value pairs

  • <DashboardUnit> is the block of XML that gets rendered on the page

  • scores={gameScores} : the scores attribute gets a value from the gameScores object that was defined earlier

Since JSX is JavaScript, identifiers such as class and for are discouraged as XML attribute names.

  • You should use DOM property names like className and hmtlFor instead

  • Basic React structure :

             let text = “lorem ipsum……”;
             reactDOM.render(
                <div>
                   <a href=“#” className=“button”>Button</a>
                  <div>{text}</div>
                </div>,
              document.getElementById(‘dom-node’)
             );
    
  • text is a variable that currently holds some lorem ipsum text

  • reactDOM.render( ) : This function is used to declare the block of code that is used to render a React component into the DOM

  • {text} : To render an expression you always need the curly braces

  • document.getElementById(‘dom-node’) : Selects the DOM node that the component interacts with

  • The block of code above should render as an anchor tag (or button) with some text right below it

  • HTML tags vs. React Components :

  • To render an HTML tag use lower-case tag names (like usual) E.g

            const myDiv  =  <div className=“foo” />;
            ReactDOM.render( myDiv, document.getElementById(‘dom-node’) );
    
  • a <div> is stored in a const called myDiv and then rendered using the ReactDOM.render() function

  • The function takes two args, the <div> stored in myDiv and document.getElementById() method which is used to select the node in the DOM tree that React interacts with

  • To render a React Component you need to declare a local variable in Pascal case E.g

             const MyComponent  =  React.createClass( {/*….*/} );
             const myElement        =  <MyComponent someProperty={true} />;
             ReactDOM.render( myElement, document.getElementById(‘dom-node’) ) ;
    
  • The React.createClass() function is initiated as a value for MyComponent & this enables MyComponent to be recognized as a React component class.

  • After the React component has been created it is then passed as a value to a const called myElement. A property has also been defined and the value has been set to a boolean (someProperty={true})

  • The ReactDOM.render() function then takes two args; the React component that was stored in myElement and document.getElementById() method which selects the specific DOM node that React interacts with

Virtual DOM

  • React uses the virtual DOM to clone a node that already exists in the DOM

  • Subtrees are created in the virtual DOM and then rendered based on state changes

  • When a state change occurs two things happen:

    • React runs a diff to check what changed

    • Then it updates the DOM based on the result of that diff (Referred to as reconciliation)

  • The Virtual DOM is considered the magic behind React. It batches DOM operations to keep everything quick and snappy because DOM manipulation is SLOW

  • Once a state is changed it triggers the diff algorithm to check all components, re-rendering only those that have changed properties.

Boilerplate Notes

  • When naming the Component.jsx file always make sure to use [PascalCase]((http://c2.com/cgi/wiki?PascalCase) E.g : Hello.jsx, World.jsx or HelloWorld.jsx

  • Don’t render code within the Component.jsx file. For instance this should be avoided:

// Hello.jsx
import React from 'react';
import ReactDOM from 'react-dom';

class Hello extends React.Component{
    render(){
        return <h1>Hello</h1>
    }
}

ReactDOM.render(<Hello/>, document.querySelector('#hello'));
  • The block above should be avoided because the Component.jsx file must be a module and it must export the code that is meant to be public. E.g
// Hello.jsx
import React from 'react';

class Hello extends React.Component{
    render(){
      return <h1>Hello</h1>
    }
}

export default Hello;
  • By doing this you allow the modular component to be included via import e.g
// index.js
import Hello from ./Components/Hello/Hello.jsx’; // <Hello />

import WhateverDefaultExists from ./Components/Hello/Hello.jsx’ // <WhateverDefaultExists />
  • In ES6 there are two kinds of exports: named exports (several per module) and default exports (one per module)

  • Use named exports when your module needs to export multiple variables/functions, and use a default export when your module only needs to export one variable/function

  • In a nutshell, ./webpack.config.js will define the entry file(s) (index.js), and the entry file executes code while all other files simply export modules

  • This applies to ALL JS modules not just React Components

  • Each component has a Component.css file that contains local scoped styles (specific to that component). E.g
-Components
    -Hello
      -Hello.css
      -Hello.jsx
  • To scope the styles to the Hello component we need to structure Hello.jsx similar to :
import React from 'react';
import CSSModules from 'react-css-modules';
import styles from './Hello.css';

class Hello extends React.Component {
    render() {
      return <h1 className='test' styleName='test'>Hello {this.props.name}</h1>;
    }
}

export default CSSModules(Hello, styles);
  • The CSSModules component is imported to enable the use of styleName prop. In the block above the difference between className and styleName is that the former is meant to be a global CSS class while the latter is meant to be CSS Modules class (locally scoped to the Hello component).

  • By adopting this approach there is a clear distinction between global CSS and CSS Modules. The output looks similar to :

  <h1 class="test Components-World-___World__test___1lmut">Hello</h1> 

Working with Props

  • Each component has a set of propTypes that define certain attributes/properties associated with that given component

  • propType is a static method declared within a React.Component, it defines the properties and their type/requirement

  • React.PropTypes is the object that defines the validators

// Component.jsx
static propTypes = {
   myProp: PropTypes.string,
   myRequiredProp: PropTypes.string.isRequired
};
//Component.js
class Hello extends React.Component {
    render() {
      return <h1 className='test' styleName='test'>Hello {this.props.name}</h1>;
    }
}

//entry.js
ReactDOM.render(<Hello name="Test" />, hello);
  • The PropTypes method is part of the React object and so it becomes available once React has been imported into a component

  • It is considered best practice to define properties specific to a component with the respectful React.PropTypes validator as this helps with debugging especially when an expected prop is missing or has the wrong type

  • A common way to use propTypes is similar to :

//Component.jsx
class Component extends React.Component{
  static propTypes = {
    name: React.PropTypes.isRequired
  }; 
  static defaultProps = {
    name: 'world'
  };
  render() {
    return <div>hello {this.props.name}</div>
  }
}
  • What this does is to tell React that name is a PropType that needs to have a value in order to render the component (isRequired).

  • If there is no value passed it will return an error in your console

  • To prevent syntax repetition when using PropTypes you can destructure the PropTypes method from the React object in order to use it without reference

  • This can be done when you import React into the Component.jsx file E.g

import React, { PropTypes } from 'react';

  • Now PropTypes can be accessed without referencing the React object E.g
static propTypes = {
  title: PropTypes.isRequired
};
  • As opposed to :
static propTypes = {
  title: React.PropTypes.isRequired
};        
  • As mentioned earlier, you can use the this keyword to access properties of a component. Using the title example above the code may look like this :
  <h3 styleName='title'>{this.props.title}</h3> 
  • To prevent this syntax repetition we can use destructuring again to extract this.props from the expression E.g
...
render(){
  const { title, children, isOpen } = this.props;
  return <h3 styleName='title'>{title}</h3>;
}   
  • What happened is that we have declared some constants (const) associated with our component and these constants also happen to be props that we defined earlier. So we can extract the props from the expression this.props so that they can be used without reference. They are still associated with the component.

More on Destructuring

class FancyDiv extends React.Component {
  render() {

    const { foo, ...otherProps } = { foo: 'foo', bar: 'bar', 'xyz': 111 };
    foo; // returns 'foo'
    otherProps; // { bar: 'bar', 'xyz': 111 }
    otherProps.foo // undefined
    otherPops.bar; // 'bar'
    otherPops.xyz; // 111
    
    const foo = 'foo';
    const bar = 'bar';
    const myObject = { foo, notBar: bar };     // { foo: 'foo', notBar: 'bar' }


    const { className, children, ...props } = this.props;
    const actualClassName = 'goodbye ' + className;
    return <div className={actualClassName} {...props}>{children}</div>;
  }
}

ReactDOM.render(<FancyDiv className="hello">Hello!</FancyDiv>, dom-node); 

To further understand destructuring I'm going to explain what is going on in the code above :

  • We have defined a constant (const) and assigned an object as its value

  • Inside that const there is a variable that looks like this : ...otherProps. It is called a spread operator

  • The spread operator is used to extract values from the object that was assigned, and since foo has been destructured it is not extracted into the spread operator.

  • So otherProps.foo returns undefined

  • While otherProps returns { bar: 'bar', 'xyz': 111 }

  • You can access a key in the object using otherProps like so :

 otherProps.bar; //returns 'bar'
 otherProps.xyz; //returns 111 
  • Let's see how this may be used with a component:
const { className, children, ...props } = this.props;
const actualClassName = 'goodbye ' + className;
return <div className={actualClassName} {...props}>{children}</div>;

ReactDOM.render(<FancyDiv className="hello">Hello!</FancyDiv>, accordion);
  • In the code above we destructure className, children, ...props from the expression (this.props)

  • We then define a new constant (const) and assign it a string (goodbye) and a variable that was extracted earlier (className)

  • When the next line runs, the <div> is rendered to the page, we assign that new constant as the className and then pass along the variable ...props as an attribute to the same <div>

  • Since there is already a className variable that was destructured in our original set of constants we have access to it through this.props.

  • So the code that gets run will look similar to : this.props.className, thereby referencing another className on the <div> and this makes it possible to have those two class names associated with the same <div> E.g

        <div class="goodbye hello">
  • The hello is passed as a value to the 'className' attribute when the <div> is rendered

Adding component props

Let's update the example above to accept properties.

  • Import React.PropTypes for prop validation
  • Add static propTypes to define available props
  • Add static defaultProps to define default values

MyComponent.js

import React, { Component, PropTypes } from 'react';

class MyComponent extends Component {
    static propTypes = {
        name: PropTypes.string.isRequired
    };
    static defaultProps = {
        name: 'World'
    };
    render() {
        return <div>Hello {this.props.name}!</div>;
    }
}

export default MyComponent;

example.js

import React from 'react';
import ReactDOM from 'react-dom';
import MyComponent from './MyComponent.js';
const el = document.getElementById('example');

// <div>Hello World!</div>
ReactDOM.render(<MyComponent />, el);

// <div>Hello Friend!</div>
ReactDOM.render(<MyComponent name="Friend" />, el);

Passing down standard attributes along with props

A common pattern when building React components is to extend a native DOM element, allowing any standard attribute to be passed down.

In the example above, we may want to set className or style, however these attributes will be ignored as render does not pass these properties to the <div> being returned.

The code can be re-written to use object destruction to extract our custom defined props and pass through all other properties.

class MyComponent extends Component {
    render() {
        const { name, ...props} = this.props;
        return <div {...props}>Hello {name}!</div>;
    }
}

Now any extra props will be passed down to the <div> being rendered...

// <div class="myClass">Hello World!</div>
ReactDOM.render(<MyComponent className="myClass" />, el);

// <div style="background: lime;">Hello World!</div>
ReactDOM.render(<MyComponent style={{ background: 'lime' }} />, el);

Working with children

The DOM is a nested tree of elements. Separate your UI into multiple components to handle their own rendering.

ListComponent.js

class ListComponent extends Component {
    static propTypes = {
        children: PropTypes.node.isRequired
    };
    render() {
        const { children } = this.props;
        return <ul>{children}</ul>;
    }
}

export default ListComponent;

ItemComponent.js

class ItemComponent extends Component {
    static propTypes = {
        name: PropTypes.string.isRequired
    };
    render() {
        const { name } = this.props;
        return <li>{name}</li>;
    }
}

export default ItemComponent ;

example.js

// <ul>
//     <li>Item #1</li>
//     <li>Item #2</li>
//     <li>Item #3</li>
// </ul>
ReactDOM.render(<ListComponent>
    <ItemComponent name="Item #1" />
    <ItemComponent name="Item #2" />
    <ItemComponent name="Item #3" />
<ListComponent>, el);

When creating dynamic children, the key prop must be specified to uniquely identify each child node. For example, the above ListComponent can written without the need for ItemComponent.

ListComponent.js

class ListComponent extends Component {
    static propTypes = {
        items: PropTypes.array.isRequired
    };
    render() {
        const { items } = this.props;
        return <ul>{items.map((item, i) => {
            return <li key={i}>{item}</li>;
        })}</ul>;
    }
}

example.js

const items = ['Item #1', 'Item #2', 'Item #3'];
ReactDOM.render(<ListComponent items={items} />, el);

Event handling

Here is an example of a component that requires a callback property onButtonClick to handle the onClick event. Key notes:

ButtonComponent.js

class ButtonComponent extends Component {
    static propTypes = {
        text: PropTypes.string.isRequired,
        onButtonClick: PropTypes.func.isRequired
    };
    static defaultProps = {
        type: 'button',
        text: 'Click Me!',
        onButtonClick() {
            alert('Button Clicked!');
        }
    };
    constructor(props) {
        super(props);
        this.handleClick = this.handleClick.bind(this);
    }
    handleClick(e) {
        e.preventDefault();
        this.props.onButtonClick();
    }
    render() {
        const { text, onButtonClick, ...props } = this.props;
        return <button onClick={this.handleClick} {...props}>{text}</button>;
    }
}

example.js

// <button type="button">Click Me!</button>
ReactDOM.render(<Button>, el);

// Pass onButtonClick prop
const onButtonClick= () => alert('Clicked Me!');
ReactDOM.render(<Button handleClick={handleClick}>, el);

Working with className

More than often you'll find yourself creating components that set a className on their rendered DOM element.

class ButtonComponent extends Component {
    render() {
        return <button className="btn" {...this.props} />;
    }
}

This is fine and dandy:

<ButtonComponent /> === <button className="button" />

Until you want to specify an additional className prop...

<ButtonComponent className="blue" /> === <button className="button" />
<ButtonComponent className="blue" /> !== <button className="button blue" />

The component must be updated to handle className as intended. In this example, we intend to concatenate both class names together:

class ButtonComponent extends Component {
    static propTypes = {
        className: PropTypes.string.isRequired
    };
    static defaultProps = {
        className: '' // If we did not provide a default value, render would need to check if undefined
    };
    render() {
        const { className, ...props } = this.props;
        const buttonClasses = 'btn ' + className;
        return <button className="btn" {...props} />;
    }
}

This pattern can be applied to any prop, className is just used as an example here.

Working with className (Cont'd)

classnames.

a simple utility for conditionally joining classNames together.

Let's extend on the example above...

  1. Add isClicked prop to toggle the className "is-active"
  2. Let's use classnames as it will help us here.
import classNames from 'classnames'; // import module

class ButtonComponent extends Component {
    static propTypes = {
        className: PropTypes.string, // removed "isRequired" as classnames will handle undefined values
        isClicked: PropTypes.bool
    };
    static defaultProps = {
        isClicked: false // this line is optional, classnames will handle undefined values
    };
    render() {
        const { className, isClicked, ...props } = this.props;
        const classList = classNames('button', className, { 'is-active': isClicked });
        return <span className={classList} {...props} />;
    }
}

Now we have a configurable button...

<ButtonComponent /> === <button className="button" />
<ButtonComponent isClicked={true} /> === <button className="button is-active" />
<ButtonComponent className="red" /> === <button className="button red" />
<ButtonComponent className="blink" isClicked={true} /> === <button className="button is-active blink" />

CSS Modules

CSS Modules are awesome. If you are not familiar with CSS Modules, it is a concept of using a module bundler such as webpack to load CSS scoped to a particular document. CSS module loader will generate a unique name for a each CSS class at the time of loading the CSS document (Interoperable CSS to be precise). To see CSS Modules in practice, webpack-demo.

In the context of React, CSS Modules look like this:

import React from 'react';
import styles from './table.css';

export default class Table extends React.Component {
    render () {
        return <div className={styles.table}>
            <div className={styles.row}>
                <div className={styles.cell}>A0</div>
                <div className={styles.cell}>B0</div>
            </div>
        </div>;
    }
}

Rendering the component will produce a markup similar to:

<div class="table__table___32osj">
    <div class="table__row___2w27N">
        <div class="table__cell___2w27N">A0</div>
        <div class="table__cell___1oVw5">B0</div>
    </div>
</div>

and a corresponding CSS file that matches those CSS classes.

Source: https://www.npmjs.com/package/react-css-modules#css-modules

Working with styles

The style property accepts an object that maps to CSS styles. This will output inline style attribute on the generated DOM element.

class MyComponent {
    render() {
        const styles = { backgroundColor: 'black', color: 'lime' };
        return <div style={styles}>Hello World!</div>;
    }
}

Styles can be dynamic based on the component's current properties.

class MyComponent {
    static defaultProps = {
        bgColor: 'black',
        txtColor: 'lime'
    };
    render() {
        const { bgColor, txtColor } = this.props;
        const styles = { backgroundColor: bgColor, color: txtColor };
        return <div style={styles}>Hello World!</div>;
    }
}

Here is another example..

class MyComponent {
    static defaultProps = {
        isBig: false
    };
    render() {
        const { isBig} = this.props;
        const styles = isBig ? { fontSize: '300%' } : null;
        return <div style={styles}>Hello World!</div>;
    }
}

Gotchas

External SVG

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="sprite">
  • The SVG may not get rendered without the namespace in the opening tag

Further reading :

###React Notes 2

Sources

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment