dev-notes/JavaScript/React/React.md
Marcello Lamonaca 17bf5fe2de
Add React Notes (#1)
* Start React notes

* useEffect & custom hooks notes

* Add React-Router Notes

* Spread operator cloning improvements

* Clarify notation for object with same key-value pair

* Add Redux notes

* Fix: translation

* Add details to event notes

* Add details to useEffect and class components

* Notes on async operations

* Fix: import <script> tag type

* Fix typos

* Add React Tests Notes

* Redux Tests Notes

* generalize paths

* Fix react testing library import

* Improve Store notes

* add missing ;

* Fix swapped examples for thunk and action tests

* Improve variable names

* Typo fixes
2021-03-29 19:34:47 +02:00

5 KiB

React

Components

Thera are two types of react components:

  • Function Components
  • Class Components

Both types can be stateful and have side effects or be purely presentational.

// functional component
const Component = (props) => {
    return (
        <domElementOrComponent... />
    );
}

// class component
class Component extends React.Component {
    return (
        <domElementOrComponent... />
    );
}

NOTE: a component name must start with an uppercase letter.

Every components has two inputs: props and state. The props input is explicit while the state is implicit. State is used to determine the chages. Within the component state can be changed while the props object represent fixed values.

JSX syntax can resable HTML bat gets converted to pure JavaScript before being sent to the browser:

// JSX
const element = (
  <h1 className="greeting">Hello, world!</h1>
);

// compiled JS shipped to browser
const element = React.createElement(
  'h1',  // HTML tag name
  {className: 'greeting'},  // attrs as JSON
  'Hello, world!'  // tag content (can be nested component)
);

App Entrypoint

ReactDOM.render(
    <RootComponent />,
    // same as
    React.createElement(RootComponent, null);

    document.getElementById("RootNode_parentId")  // add to DOM
);

Dynamic Expressions

<tag>{expression}</tag>  // expression is evalueted an it's result is displayed
<tag onEvent={funcReference}>{expression}</tag>
<tag onEvent={() => func(args)}>{expression}</tag>

Props

<Component propName={value} />  // pass a value the component
<Component propName={funcreference} />  // pass a function to the component

function Component(props) {
    // use props with {props.propName}
}

class Component extends React.Component{
    // use props with {this.props.propName}
    render()
}

Simple Function Component

// Button.js
import { useState } from "react";

function Button() {
    const [count, setCount] = useState(0);  // hook

    const handleCLick = () => setCount(count + 1);  // logic

    // JSX
    return (
        <button onClick={handleCLick}>
            {count}
        </button>
    );
}

export default Button;

Simple Class Component

class Button extends React.Component {

    state = {count: 0};
    //or
    constructor(props) {
        super(props);
        this.state = {count: 0};
    }

    componentDidMount() {}  // called on successful component mount
    
    handleClick = () => {
        this.setState({ count: this.state.count + 1 });
    }
    // or
    handleClick = () => {
        this.setState((state, props) => ({ count: state.count + props.increment }) );
    }

    render(){
        return (
        <button onClick={this.handleClick}>
            {this.state.count}
        </button>
        );
    }
}

Nesting Compnents

import { useState } from "react";

function Button(props) {
  return (
      <button onClick={props.onClickFunc}>
          +1
      </button>
  );
}

function Display (props) {
  return (
      <div>{props.message}</div>
  );
}

function App() {

    // state must be declare in the outer component it can be passed to each children
    const [count, setCount] = useState(0);
    const incrementCounter = () => setCount(count + 1);

    return (
        <div className="App">
            <Button onClickFunc={incrementCounter}/>
            <Display message={count}/>
        </div>
    );
}

export default App;

User Input (Forms)

function Form() {
    const [userName, setUserName] = useState("");

    handleSubmit = (event) => {
        event.preventDefault();
        // ...
    }

    return(
        <form onSubmit={handleSubmit}>
            <input
                type="text"
                value={userName}  // controlled component
                onChange={(event) => setUserName(event.target.value)}  // needed to update UI on dom change
                required
            />
            <button></button>
        </form>
    );
}

Lists of Components

// ...
    <div>
        {array.map(item => <Component key={uniqueID}>)}
    </div>
// ...

NOTE: The key attribute of the component is needed to identify a particular item. It's most useful if the list has to be sorted.

Hooks

useState

Hook used to create a state object.

useState() results:

  • state object (getter)
  • updater function (setter)
const [state, setState] = useState(default);

useEffect

Hook used to trigger an action on each reder of the component or when one of the watched items changes.


useEffect(() => {
    // "side effects" operations

    return () => {/* clean up side effect */}  // optional
}, [/* list of watched items, empty triggers once */]);

Custom Hooks

// hook definitions
const useCutomHook = () => {
    // eventual state definitions

    // eventual function definitions

    // ...
    
    return { obj1, obj2, ... };
}

const Component(){
    // retrieve elements from the hook
    const {
        obj1,
        obj2,
        ...
    } = useCustomHook();
}