react_basic

Basics

JSX

JSX is a syntax extension to JavaScript, it combines js with html, it only comes in when writes your js code like this, but not all browsers support such extension, if browser does not support it, you can convert it to native js code by Babel at server-side or at client side by include Babel.js for that website, we recommend do this in server-side as we can do some optimization. but Most mordent browsers support JSX now.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const element = <h1 className="greating"> Hello </h1>;
// React doesn’t require using JSX, same as below if browser does not support JSX
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);

//the final object.
const element = {
// type and props are two attributes of each React Element!!!!
type: 'h1',
props: {
className: 'greeting',
children: 'Hello, world!'
}
};

Embedding Expressions in JSX
You can put any valid JavaScript expression inside the curly braces in JSX. For example, 2 + 2, user.firstName, or formatName(user) are all valid JavaScript expressions.

1
2
3
4
5
6
const name = 'Josh Perez';
const element = <h1>Hello, {name}</h1>;
const element = <h1>Hello, {2+2}</h1>;
const element = <h1>Hello, {fun(2)}</h1>;
const element = <h1>Hello, {false}</h1>;
const element = <img src={user.avatarUrl}></img>;

React DOM uses camelCase property naming convention instead of HTML attribute names

  • class becomes className in JSX, and tabindex becomes tabIndex
  • built-in tag attributes may be renamed if it has two words or one word that is reserved by JSX

There are several rules about JSX

  • <tags> are elements

    • JSX <tags> map to calls to React.createElement().
    • Use <lowercase /> tags when you need a DOM elements, and tags for component elements
  • JSX children become child elements

    • they become the element’s props.children
  • Attributes are props

    • Use “” quotes when your props are strings
    • Use {} braces when your props are literals or variables
  • {} interpolates children

    • When a pair of {} braces is encountered within a JSX element, it’s value will be interpolated in as a child.
    • let Hello = (props) => <div>Hello, {props.to}
  • Empty <> tags

    • A pair of empty <> and </> tags get’s turned into a React.Fragment element, i.e. an element that doesn’t map to DOM nodes
  • {…object} acts like Object.assign()

    • Passing {…object} as an attribute will add all of the properties of the object as separate attributes
  • Function Component

    Function Component has no state, is cheap, always use it if no state needed or no life cycle hooks needed.
    Function Component must return a React element or null to prevent render there are several ways to achieve this.

    Actually you can use any name to replace props but props seems have the right meaning. any object name would be fine.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    function Welcome(props) {
    console.log("children:",props.children);
    return <h1>Hello, {props.name}</h1>;
    }

    //All attributes will store at an object and passed to Constructor!!
    // {name: "jason"} passed as props= {name: "jason"}
    const elment_jason = <Welcome name="jason"/>;
    //anything inside <Welcome>xxx</Welcom> are called children
    const elment_jason = <Welcome name="jason"> children</Welcome>

    //operator spreading in parameter
    function Hi({name:name1, ...other}) {
    // other is also an object {id: 12, city: "shanghai"}
    return <h1>Hello, {name1} {other.id} {other.city}</h1>;
    }

    //The other same way
    function Hi(props) {
    const {name: name1, ...other} = props;
    // other is also an object {id: 12, city: "shanghai"}
    return <h1>Hello, {name1} {other.id} {other.city}</h1>;
    }


    const element_jason = <Hi name="jason" id={12} city="shanghai"/>;

    //pass each attr in other object separately same as above
    const other = {id: 12, city: "shanghai"};
    const element_jason = <Hi name="jason" {...other} />
    // if there are several statements, use {} and return is a must
    //otherwise, one statement, must omit {} and return at same time, it's required by arrow function
    const Greeting = (props) => <h1>Hello, {props.name}</h1>;
    const Greeting = (props) => <h1>Hello, {props.name}</h1>;
    const Greeting = (props) => { return <h1>Hello, {props.name}</h1>;};
    const Greeting = (props) => {
    if(props.warn) {
    return null;
    }
    return <h1>Hello, {props.name}</h1>;
    }

    props check and default value

    When we declare an property of an React element, we can add some checks and default value for it.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import PropTypes from 'prop-types';
    function Welcome(props) {
    return <h1>Hello, {props.name}: {props.id}</h1>;
    }

    Welcome.defaultProps = {
    name: "josh"
    }
    Welcome.propTypes = {
    id: PropTypes.element.isRequired
    }

    const hiJason = <Welcome id={12} name="Jason"/>;
    const hiJosh = <Welcome id={16} />;

    Note this way also works for Class Component as well!!!

    More step of function component.

    As normally, Function Component is not stateful or no lifecycle hook like Class Component, but class Component is heavy, is there a way we can use state and lifecycle hook like Class does. that’s why Hooks comes into place which let you use state and other React features without writing a class(use them in Function.

    Hooks are functions that let you “hook into” React state and lifecycle features from function components. Hooks don’t work inside classes.

    State Hook

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import React, { useState } from 'react';
    function Example() {
    // Declare a new state variable, which we'll call "count"
    // you can call useState(0) many times if you need more states
    // const [number, setNumber] = useState(12); 12 is initial value
    // const [data, setData] = useState({ob: 12}); {ob: 12};

    //then update count in callback by call setCount(1)
    const [count, setCount] = useState(0);

    return (
    <div>
    <p>You clicked {count} times</p>
    <button onClick={() => setCount(count + 1)}>
    Click me
    </button>
    </div>
    );
    }

    useState is a Hook. We call it inside a function component to add some local state to it. React will preserve this state between re-renders. useState returns a pair: the current state value and a function that lets you update it. You can call this function from an event handler or somewhere else. It’s similar to this.setState in a class, except it doesn’t merge the old and new state together

    Effect Hook
    The Effect Hook, useEffect, adds the ability to perform side effects from a function component. It serves the same purpose as componentDidMount, componentDidUpdate, and componentWillUnmount in React classes, but unified into a single API.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    import React, { useState, useEffect } from 'react';
    function Example() {
    const [count, setCount] = useState(0);

    // Similar to componentDidMount and componentDidUpdate:
    // also you can call useEffect many times.
    useEffect(() => {
    // Update the document title using the browser API
    // this is call when DidMount and DidUpdate
    document.title = `You clicked ${count} times`;

    //useEffect only takes on parameter, callback but if the callback return a function
    //that returned function is called when DidUnmount!
    });
    return (
    <div>
    <p>You clicked {count} times</p>
    <button onClick={() => setCount(count + 1)}>
    Click me
    </button>
    </div>
    );
    }

    Effects are declared inside the component so they have access to its props and state. By default, React runs the effects after every render — including the first render.

    Rules for Hooks

    • Only call Hooks at the top level. Don’t call Hooks inside loops, conditions, or nested functions.
    • Only call Hooks from React function components. Don’t call Hooks from regular JavaScript functions.

      Class Component

      Class Component has a built-in state attribute, an object stores all states of such component, it must
    • inherit from React.Component
    • call super(props) if has custom constructor, you can also access this.props in its method!!!
    • have a render() method that returns an React element
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class Clock extends React.Component {
    constructor(props) {
    super(props);
    //init the state object, state is a object with many key:value pairs
    this.state = {date: new Date()};
    }
    render() {
    const now = this.state.date.toLocaleTimeString();
    return <div><h2>It is {now}</h2></div>;
    }
    xxx() {
    //add another state Id, setState is built-in method
    this.setState({Id: 2});
    //another way to call setState with callback which must return {k: v} object used to update state store
    //setState((prestate, preprops)=>{ return {id: prestate.id + 1};});
    }
    }

    React element life cycle hooks

    When we mount a React element to a Dom node(mount point), other remove it from that, several event happens, React already added some hooks to each event, you just need to implement that hook if you want to do thing.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    class Clock extends React.Component {
    constructor(props) {
    super(props);
    this.state = {
    counter: 12
    }
    }

    componentDidMount() {
    //setup thing
    }

    componentWillUnmount() {
    // do cleanup
    }
    render() {
    // pass state to child as props
    return <Sub counter={this.state.counter}/>;
    }

    Data(state) Flow Down

    state is often called local or encapsulated, always set it and access it in the component who owns it, if child wants to access it, pass it as props to the child.

    Lift state up

    Two child elements wants to modify same state(shared same one), this is impossible if each child has a state as it’s local so lift state up, after Lift state up, parent elements owns this state, that means parent is only person who can call setState to update it, but two children want to modify it and see same, so parent passes two props to child, one is the value, the other is callback defined by parent, each child calls the callback which setState() to update the shared state which owns by parent. child update parent’s state by parent’s callback.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    class AButton extends React.Component {
    constructor(props) {
    super(props);
    this.update = this.update.bind(this);
    }
    update() {
    //must bind otherwise, you can not see this here!!!
    this.props.add(1);
    }
    render() {
    return <button onClick={this.update}>A:{this.props.num}</button>;
    }
    }
    class BButton extends React.Component {
    constructor(props) {
    super(props);
    this.update = this.update.bind(this);
    }
    update() {
    this.props.add(2);
    }
    render() {
    return <button onClick={this.update}>B:{this.props.num}</button>;
    }
    }
    class GButton extends React.Component {
    constructor(props) {
    super(props);
    this.state = {
    counter: 0
    };
    this.add = this.add.bind(this);
    }
    add(n) {
    this.setState((prestate) => {return {counter: prestate.counter + n}});
    }
    render() {
    return <div> <AButton num={this.state.counter} add={this.add}/> <BButton num={this.state.counter} add={this.add}/></div>;
    }
    }
    ReactDOM.render(<GButton/>, document.getElementById('root'))

    Composition vs Inheritance

    We recommend using composition instead of inheritance to reuse code between components, here are some good example to use composition

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    function FancyBorder(props) {
    return (
    //put children directly here
    <div className={'FancyBorder FancyBorder-' + props.color}>
    {props.children}
    </div>
    );
    }
    function WelcomeDialog() {
    return (
    //<FancyBorder >xxx</FancyBorder> xxx are children
    <FancyBorder color="blue">
    <h1 className="Dialog-title">
    Welcome
    </h1>
    <p className="Dialog-message">
    Thank you for visiting our spacecraft!
    </p>
    </FancyBorder>
    );
    }

    //instead of use children as a whole, we use specific property set by caller with proper 'reuse' element
    function SplitPane(props) {
    return (
    <div className="SplitPane">
    <div className="SplitPane-left">
    {props.left}
    </div>
    <div className="SplitPane-right">
    {props.right}
    </div>
    </div>
    );
    }
    function App() {
    //React Element as in expression
    return <SplitPane left={<Contacts />} right={ <Chat />}/>;
    }

    //combine attribute and children
    function Dialog(props) {
    return (
    <FancyBorder color="blue">
    <h1 className="Dialog-title">
    {props.title}
    </h1>
    <p className="Dialog-message">
    {props.message}
    </p>
    {props.children}
    </FancyBorder>
    );
    }

    class SignUpDialog extends React.Component {
    constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.handleSignUp = this.handleSignUp.bind(this);
    this.state = {login: ''};
    }

    render() {
    return (
    <Dialog title="Mars Exploration Program"
    message="How should we refer to you?">
    <input value={this.state.login} onChange={this.handleChange} />
    <button onClick={this.handleSignUp}> Sign Me up! </button>
    </Dialog>
    );
    }

    handleChange(e) {
    this.setState({login: e.target.value});
    }

    handleSignUp() {
    alert(`Welcome aboard, ${this.state.login}!`);
    }
    }

    Controlled Component

    In HTML, form elements such as <input>, <textarea>, and <select> typically maintain their own state and update it based on user input. In React, mutable state is typically kept in the state property of components, and only updated with setState().

    We can combine the two by making the React state be the “single source of truth”. Then the React component that renders a form also controls what happens in that form on subsequent user input. An input form element whose value is controlled by React in this way is called a “controlled component”. React does not want HTML dom element maintain their own state, React wants state be ‘single source of truth’, that why React wants to control the value(state property).

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    class NameForm extends React.Component {
    constructor(props) {
    super(props);
    this.state = {value: ''};
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
    }

    handleChange(event) { this.setState({value: event.target.value}); }
    handleSubmit(event) {
    alert('A name was submitted: ' + this.state.value);
    event.preventDefault();
    }
    // value attribute comes from state which is controlled by React.
    render() {
    return (
    <form onSubmit={this.handleSubmit}>
    <label>
    Name: <input type="text" value={this.state.value} onChange={this.handleChange} />
    </label>
    <input type="submit" value="Submit" />
    </form>
    );
    }
    }

    Uncontrolled component

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    class NameForm extends React.Component {
    constructor(props) {
    super(props);
    this.handleSubmit = this.handleSubmit.bind(this);
    //create Ref
    this.input = React.createRef();
    }

    handleSubmit(event) {
    //ref.current points to linked element
    alert('A name was submitted: ' + this.input.current.value);
    event.preventDefault();
    }

    render() {
    //link the Ref to <input> element
    return (
    <form onSubmit={this.handleSubmit}>
    <label> Name: <input type="text" ref={this.input} /></label>
    <input type="submit" value="Submit" />
    </form>
    );
    }
    }

    React Events

    React implements the standard Event API(W3C Spec) that’s same as Dom event except React named with different styles.

    • React events are named using camelCase, rather than lowercase
    • With JSX you pass a function as the event handler, rather than a string.
      In html
      1
      2
      3
      <button onclick="activateLasers()">
      Activate Lasers
      </button>

    In React

    1
    2
    <button onClick={activateLasers}>  Activate Lasers
    </button>

    Note: if you refer to a method without () after it, such as onClick={this.handleClick}, you should bind that method otherwise you can’t use this in that handler

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    //*experimental*
    class LoggingButton extends React.Component {
    handleClick = () => {
    console.log('this is:', this);
    }
    render() {
    return (
    <button onClick={this.handleClick}>
    Click me
    </button>
    );
    }
    }

    // use this way.
    class LoggingButton extends React.Component {
    constructor(props) {
    this.handleClick = this.handleClick.bind(this);
    }
    handleClick(e) {
    console.log('this is:', this);
    }
    render() {
    return (
    <button onClick={this.handleClick}>
    Click me
    </button>
    );
    }
    }

    class LoggingButton extends React.Component {
    handleClick () {
    console.log('this is:', this);
    }
    render() {
    // Never use this way.
    return (
    <button onClick={ ()=> this.handleClick() }>
    Click me
    </button>
    );
    }
    }

    class LoggingButton extends React.Component {
    handleClick (id, e) {
    console.log('this is:', this);
    }
    render() {
    // use this way if you want to pass another parameter to event callback
    // by default, callback is called with only event parameter
    return (
    <button onClick={this.handleClick.bind(this, id)}>
    Click me
    </button>
    );
    }
    }

    Another difference is that you cannot return false to prevent default behavior in React. You must call preventDefault explicitly.

    {} treats special handling for Array

    As you know any expression can be in {}, if an Array in the {}, it will destruct the Array!! also note when use map, each item must has a key attribute, this is required by React.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    function NumberList(props) {
    const numbers = props.numbers;
    const listItems = numbers.map((number) =>
    //no dom node for Fragment
    <React.Fragment key={number.toString()}>
    <li>{number}</li>
    </React.Fragment>
    );
    return <ul>{listItems}</ul>
    }

    const numbers = [1, 2, 3, 4, 5];
    ReactDOM.render(
    <NumberList numbers={numbers} />,
    document.getElementById('root')
    );

    Fragments

    A common pattern in React is for a component to return multiple elements. Fragments let you group a list of children without adding extra nodes to the DOM

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    class Columns extends React.Component {
    render() {
    return (
    <>
    <td>Hello</td>
    <td>World</td>
    </>
    );
    }
    }

    // React.Fragment supports attribute but <></> not
    function Glossary(props) {
    return (
    <dl>
    {props.items.map(item => (
    // Without the `key`, React will fire a key warning
    <React.Fragment key={item.id}>
    <dt>{item.term}</dt>
    <dd>{item.description}</dd>
    </React.Fragment>
    ))}
    </dl>
    );
    }

    Use CSS module

    CSS Modules allows the scoping of CSS by automatically creating a unique classname of the format this is done Webpack, it let you use the same CSS class name in different files without worrying about naming clashes, you write CSS files like before, nothing change for you when writing CSS file.

    1
    2
    3
    4
    //button.css
    .error {
    background-color: red;
    }
    1
    2
    3
    4
    //text.css
    .text {
    color: black;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //use it in react app Button.js
    import React, { Component } from 'react';
    import styles from './button.css'; // Import css modules stylesheet as styles
    import './text.css' //not as a modules, use class directly
    class Button extends Component {
    render() {
    // reference as a js object, must use this way
    return (<div>
    <button className={styles.error}>Error Button</button>
    <button className="text">Error Button</button>
    </div>);
    }
    }

    The result in html is this

    1
    2
    3
    4
    5
    <!-- This button has red background but not red text -->
    <div>
    <button class="Button_error_ax7yz">Error Button</button>
    <button class="text">Error Button</button>
    </div>

    REF