reactReact

#ES6

@jbcazaux

http://codox.fr/formation-reactjs-es6

my name is
coffee break

Goals

goals

Versions


                    "react": "16.8.6"
                

Content

  • ES6
    • ES6 Features
    • Functional oriented programming
  • ReactJS
    • Fundamental
    • Components
    • State
    • Redux
    • Tests
    • Routing
    • Optimizations

EcmaScript 6

var, let, const


                let foo = 'bar'
                const MAX = 10
                const ttc = addTva(15)
                

let vs. const ? 0 var, 44 let, 997 const

var, let, const


                if (true) {
                    var i = 1
                } else {
                    var j = 2
                }
                console.log(i, j)
                

                const user = {}
                user.name = 'john' // (1)
                user = {name: 'lea'} // (2)
                

                const arr = ['a']
                arr.push('b') // (3)
                arr = ['a', 'b'] // (4)
                

Arrow functions


                function addOldSchool(a, b) {
                    return a + b
                }
            

                let add = (a, b) => {
                    return a + b
                }
                

                add = (a, b) => a + b
                

Arrow functions


                const squares = [0, 1, 2, 3, 4].map(x => x * x)
                console.log(squares)
                

Warning !

Not everything is supported yet

  • Polyfill (Array.flatMap(), Array.reduce(), ...)
  • Transpiler (=>, let, const, ...)

Immutability !

Please do not mutate during the training :)

Spread operator


                const odd = [1, 3, 5, 7, 9]
                const even = [2, 4, 6, 8]
                const all = [0]

                // arr.push([element1[, ...[, elementN]]])
                all.push(odd)
                all.push(even)

                console.log(all) // ?
                

                const odd = [1, 3, 5, 7, 9]
                const even = [2, 4, 6, 8]
                const all = [0]

                // arr.push([element1[, ...[, elementN]]])
                for (let i = 0; i < odd.length; i++) {
                    all.push(odd[i])
                }
                for (let i = 0; i < even.length; i++) {
                    all.push(even[i])
                }
                console.log(all) // ?
                

                    const odd = [1, 3, 5, 7, 9]
                    const even = [2, 4, 6, 8]
                    const all = [0, ...odd, ...even]
                

Spread operator


                let user = {
                    firstname: 'john',
                    nickname: 'ninja',
                    lastname: 'doe',
                    age: 21
                }

                user = {...user, firstname: 'toto'}

                console.log(user) // ?
            

Destructuring


                    const foo = {a: 1, b: 2, c: 'bar'}
                    let {a} = foo
                    console.log(a) // ?
                

Destructuring


            const user = {
                firstname: 'john',
                nickname: 'ninja',
                lastname: 'doe',
                age: 21
            }

            function getUserName(user) {
                return `${user.firstname} ${user.lastname}`)
            }
            console.log(getUserName(user))
            

            function getUserName({firstname, lastname}) {
                return `${firstname} ${lastname}`)
            }
            

            const getUserName = ({firstname, lastname}) =>
                `${user.firstname} ${user.lastname}`
            

Destructuring - nested


            const user = {
                firstname: 'john',
                lastname: 'doe',
                address: {
                    street: 'avenue des champs élysées',
                    num: '12'
                }
            }

            function getUserAddress(user) {
             return `${user.address.num} ${user.address.street}`
            }
            

            const getUserAddress = ({address: {num, street}}) =>
                `${num} ${street}`
            

Destructuring - alias


            const user = {
                firstname: 'john',
                lastname: 'doe',
                address: {
                    street: 'avenue des champs élysées',
                    num: '12'
                }
            }

            let getUserAddress = ({address: {num: foo, street: bar}}) =>
                `${foo} ${bar}`
            

Destructuring - arrays


            const maths = [x => x + 1, x => x * x, x => x * x * x]
            const [plus1, square] = maths
            plus1(41) // 42
            square(12) // 144
            

Shorthand Notation


                const foo = 42
                const bar = 1

                const longer = {
                    foo: foo,
                    bar: bar
                }
            

                const shorter = {
                    foo,
                    bar
                }
            

Promises VS. async - await


            axios.get('/users/42/items')
                .then(resp => resp.data)
                .then(items => items.map(item => item.id))
                .then(ids => {/* use the ids */})
                .catch(error => console.log(error.toString()))
            

            async function fetchItems {
                try {
                    const response = await axios.get('/users/42/items')
                    const items = response.data
                    const itemIds = items.map(item => item.id))
                    /* use the ids */
                } catch(error){
                    console.log(error.toString())
                }
            }
            

Promesses VS. async - await


            axios.get('/users/42/items')
                .then(({data : items})) => items.map(item => item.id))
                .then(ids => {/* use the ids */})
                .catch(error => console.log(error.toString()))
            

            async function fetchItems {
              try {
                const {data: items} = await axios.get('/users/42/items')
                const itemIds = items.map(item => item.id))
                /* use the ids */
              } catch(error){
                console.log(error.toString())
              }
            }
            

Reminder - currying


                let add = function(x) {
                    return function(y) {
                        return x + y
                    }
                }
                add(2)(3) // ???
            

                const add2 = add(2)
                add2(3) // = ?
            

                let add = x => y => x + y // WTF ?!
            

                let add = x =>
                    y => (x + y)
                //easy !
            

Reminder - this


                function f() {
                    this.foo // what is this ?
                }
            

react

Everything is Component

components

Hello world

hello.js
            import React from "react"

            const Hello = (props) => 
Hello {props.name} !
export default Hello
index.js
            import React from "react"
            import ReactDOM from "react-dom"
            import Hello from "./Hello"

            ReactDOM.render(
                <Hello name="world"/>,
                document.getElementById('root')
            )
            

JSX


                (props) => (
                     <li className="contact">
                          <h2 className="contact-name">{props.name}</h2>
                      </li>
                    )
                

                (props) => React.createElement('li', {className: 'contact'},
                        React.createElement('h2',
                            {className: 'contact-name'}, props.name)
                        )
                )
                

TP-01

Hello World

  1. Setup your environment
  2. Write the Hello component/function

Type checking

We can do nothing, use Typescript , Flow, Reason, or PropTypes


            import PropTypes from 'prop-types'
            const Hello = ({name}) => 

Hello {name}!

Hello.propTypes = { name: PropTypes.string.isRequired, truc: PropTypes.bool }

types: array, bool, func, number, object, string, shape, node, arrayOf(), ... https://reactjs.org/docs/typechecking-with-proptypes.html

Component's attributes


            const Counter = () => {
                let count = 0

                return <div onClick={() => count = count + 1}>
                    Count: {count}
                </div>;
            }
            
Don't try this at home !

State


           const ComponentWithState = (props) => {
            const [count, setCount] = useState(0)
            const [user, setUser] = useState(null)
            const [color, setColor] = useState('green')

            return ...
           }
           

State

Set the default state by passing an argument to useState()


            const [count, setCount] = useState(42)
          

Update a state


            const [count, setCount] = useState(0)
            setCount(10)
            setCount(prevState => prevState + 1)
            

Get current state


            const [count, setCount] = useState(0)
            count
          

State


            const Counter = () =>  {
              const [count, setCount] = useState(0)
              return <div onClick={() => setCount(prev => prev + 1)}>
                  Count: {count}
              </div>
            }
            

Props vs State

Props

  • Data from the parent component

State

  • Data that may change on user's interactions (clicks, inputs)
  • Write as many stateless components as possible
  • Data computed from props or state(do that in the function itself)
  • Components

Class components


            class ButtonApp extends React.Component {
                state = {count: 0}
                render() {
                    return <div>
                        <div>{this.props.title}</div>
                        <button>{this.props.label}</button>
                        <div>{this.state.count}</div>
                    </div>
                }
            }
            

Iterating over an array


            const MyComponent = (props) => (
                <ul>
                {
                    props.items.map(item =>
                        <li key={item.id}>{item.label}</li>)
                }
                </ul>
            )
            

useEffect

It triggers a function when props or state change


                const UserDetails = ({userId}) => {
                 const [details, setDetails] = useState(null)

                 useEffect(() => {
                  axios.get('/user/' + userId)
                   .then(({response: {data: details}}) => setDetails(details)
                 }, [userId])

                 return <div>...
                }
              

TP-02

Shopping list

  1. Display the title provided by the parent
  2. Set a default state (empty list)
  3. Set the list of items in the state when webservice has returned
  4. Display the list items in <li></li> tags
  5. Create a dedicated component for the <li> tags
  6. Bonus: Create a component that makes the ajax call, and pass the result to a child component that iterates over the list.

Bonus TP-02


            const ShoppingList = ({title}) => {
               const [items, setItems] = useState([])
               // useEffect({...}, [])
               return <ShoppingListInternal title={title}
                        items={items}/>
            }
            const ShoppingListInternal = ({title, items}) =>
            <div>
                <h2>{title}</h2>
                <ul>{items.map(item =>
                    <ShoppingItem key={item.id} item={item}/>)}
                </ul>
            </div>
            

Pass functions into the props


            const ShoppingList = ({title}) => {
                const [items, setItems] = useState([])
                // useEffect({...}, [])

                const deleteItem = () => {
                    const newItems = items.slice(1)
                    setItems(newItems)
                }
                return (<ShoppingListInternal
                        title={title}
                        items={items}
                        del={deleteItem}/>)
                }
            }
            

Use functions from the props


            const ShoppingListInternal = ({title, items, del}) => (
                <div>
                    <h2>{title}</h2>
                    <ul>
                        {
                          items.map(item => (<li onClick={del}>
                            {item.label}: {item.price}€ </li>))
                        }
                    </ul>
                </div>)
            

Get the JS event


            const ClickableApp = () => {
                const handleClick = e => {
                    console.log('click !', e)
                }
                return <div onClick={handleClick}>Click me !</div>
            }
            

            const ClickableApp = () => (
                <div
                    onClick={(e) => console.log('click !', e)}>
                    Click me !
                </div>
            )
            

With style

inline style vs css

Why not inlining css into js ?


            MyComponent = () => {
                const mystyle = {backgroundColor: '#F0ABCD'}
                return <div style={mystyle}/>
            }
            

Or you can use a global css, or a css per component (css modules with Webpack)


                import './MyComponent.css'
          

React-bootstrap

Bootstrap components for React


                <Button bsStyle="primary"
                        onClick={handleCdlick}>Go!</Button>
            

Documentation

Material-UI

React components that implement Google's Material Design


                <Button onClick={handleClick}>Go!</Button>
            

Documentation

https://material-ui.com/api/button/

Styled-Components

Visual primitives for the component age.


            const Title = styled.h1`
                font-size: 1.5em;
                text-align: center;
                color: palevioletred;
            `
            <Title>Nice title !</Title>
            

Documentation

https://www.styled-components.com/docs/api

Tips and tricks

(Workarounds)

Fragments


            render() {
                return (
                <React.Fragment>
                    <div>Pourquoi </div>
                    <span>pas ?</span>
                </React.Fragment>
                )
            }
            

            render() {
                return (
                <>
                    <div>Pourquoi </div>
                    <span>pas ?</span>
                </>
                )
            }
            

Special chars


                 Jet d{String.fromCharCode(39)}eau
            

JSX if (else)

if


                {user && 
{user.login}
}

not


                {user || 
No User
}

if-else


                {user
                    ? 
{user.login}
:
No User
}

Not better in angular :)

Comments


            const MyComponent = () => {
                // comment inside JS
                return <div>
                    {/* Comment inside JSX */}
                </div>
            }
            

Children


            ReactDOM.render(
               <ButtonApp title="My Application">Press Me! </ButtonApp>,
               document.getElementById('root')
            )
            

            export const ButtonApp = ({title, children}) => {
                return <div>
                    <div>{title}</div>
                    <button>{children}</button>
                </div>
            }
            

TP-03

Students List

  1. Create an "input" field that filters the elements of a Table
  2. Display the name of the selected student (the one which has been clicked on), or a message if no student is selected
  3. Bonus : Do a nice Pull Request to make this exercice look beautiful ;)

What we have seen so far

  • A component has props and states
  • A stateless component is simpler to write and maintain

Flux & Redux

Flux

  • Store: Store the data
  • Action: Update model
  • Dispatcher: Dispatch actions into the stores
  • View: UI Components
flux

The flow is unidirectional ! (2-way binding)

Bad Practice vs. Good Practice

best practices
  • We dont want to update other components directly
  • We can use the store (redux), the stores (flux), services (RxJs Subjects ), ...

Redux

Actions


                {type: 'INCREMENT', inc: 3}
                {type: 'ADD_TODO', text: 'Buy bread'}
                {type: 'SET_USER', user: new User(1, 'toto')}
            

Action Creator


            export const incrementor = (inc) => {
                return {type: 'INCREMENT', inc}
            }
            

            export const incrementor =
                (inc) => ({type: 'INCREMENT', inc})
            

Redux


                const userId = window.current_account.id
            

«Why use redux if we have global variables ?»

#TROLL

Redux

  • Unique store (isomorphism is easier)
  • The state of the store is read-only
  • We dispatch actions to update the state (of the store)
  • Actions are sent to the reducers
  • Reducers are puresfunctions
  • Reducers have current state and an action as parameters
  • They return a new state in an 'immutable' object
redux

Reducer


            export const display = (state = 0, action) => {
                switch (action.type) {
                    case 'INCREMENT':
                        return state + action.inc
                    case 'DECREMENT':
                        return state - action.inc
                    default:
                        return state
                }
            }
            
The state is read-only ! redux

React & Redux

Reducers composition

Naive approach


            const initialState = {user: '', counter: 0}
            export const myGlobalReducer =
                (state = initialState, action) => {
                    switch (action.type) {
                        case 'SET_USER':
                           return {...state, user: action.user}
                        case 'INCREMENT':
                           return {...state, counter: state.counter + 1}
                        case 'DECREMENT':
                           return {...state, counter: state.counter - 1}
                        default:
                           return state
                }
            }
            
Not really readable on medium/large applications...!

Reducers composition


            export const user =
                (state = User.NULL, action) => {
                    switch (action.type) {
                        case 'SET_USER':
                            return action.user
                        default:
                            return state
                    }
                }
            export const counter =
                (state = 0, action) => {
                    switch (action.type) {
                        case 'INCREMENT':
                            return state + 1
                        case 'DECREMENT':
                            return state - 1
                        default:
                            return state
                    }
            }
            

Reducers composition

Wow effect


            const init = {user: 'nobody', counter: 0}
            export const myGlobalReducer = (state = init, action) =>
                ({
                    user: user(state.user, action),
                    counter: counter(state.counter, action)
                })
            

And with a little bit of magic...


            import { combineReducers } from 'redux'
            export const myGlobalReducer = combineReducers({
                user,
                counter
            })
            

Reminder

  • Actions
  • Reducers
  • Store
  • React Components
  • Now the components must be linked to the store
redux

Hooks and Components

  • Separation of concerns: UI vs plumbery
  • Reusable technical code goes into the hooks

Hooks

useSelector


            import { useSelector } from 'react-redux'

            export const MyComponent = () => {
              const dataFromTheStore = useSelector(state => state.data)
              return <div>{data.id} / {data.info} </div>
            }
            

useDispatch


            import { useDispatch } from 'react-redux'

            export const MyComponent = () => {
              const dispatch = useDispatch()
              return <div onClick={() => dispatch({type: 'INC', inc:1})}>
                Click !
              </div>
            }
            

Connected component


            export const SelectPostalCode = () => {
              const dispatch = useDispatch()
              const currentPc = useSelector(state => state.currentPc)
              const postalCodes = useSelector(state => state.postalCodes)

              const handleOnChange = e => {
                const action = setCurrentPc(e.target.value)
                dispatch(action)
              }
              <select onChange={handleOnChange} value={currentPc}>
              {
                postalCodes.map(pc =>
                    <option key={pc} value={pc}>{pc}</option> )
              }
            </select>
            )
          

Pass the store to the whole application

All container components of the application must have access to the store.


                import React from 'react'
                import ReactDOM from 'react-dom'
                import {Provider} from 'react-redux'
                import {createStore} from 'redux'
                import {reducer} from './reducers/index'
                import {App} from './components/app'

                const store = createStore(reducer)

                ReactDOM.render(
                    <Provider store={store}>
                        <App/>
                    </Provider>,
                    document.getElementById('app')
                )
          

TP-04

Shopping list - bis

  1. Install redux-dev-tools
  2. Like TP-02, but use the state of the store rather than the state of the component, and initializing the list (with synchronous call)
  3. Create an action creator setItems which returns an ItemAction
  4. Create a reducer items which takes an ItemAction as input
  5. Create a global reducer thanks to combineReducer
  6. Create the container and the presentational component
  7. Bonus: Create a button that adds a new Item in the list

Asynchronous Actions

Until now

  • Redux allows to dispatch only Objects (actions)
  • redux-thunk or redux-saga allow to dispach functions, and so asynchronous actions or actions with conditional statements. They are called middlewares.

redux-thunk

Configuration


                import {reducer} from './reducers/index'
                import {createStore, applyMiddleware} from 'redux'
                import thunk from 'redux-thunk'

                const store = createStore(
                reducer,
                applyMiddleware(thunk)
                )

                

ThunkActionCreator


            function incrementCreator(inc) {
                return {
                    type: 'INCREMENT_COUNTER',
                    inc: inc
                }
            }

            function incrementAsyncCreator(inc) {
                return dispatch => setTimeout(
                        () => dispatch(incrementCreator(inc)),
                        1000)
            }
            

To summarize

A ThunkAction is a function that gets dispatch() and getState() as parameters, and returns the result of the call to dispatch().

Example


            const getItems =
                (userId) => axios.get('users/' + userId + '/items')
                    .then(resp => resp.data)
                    .catch(error => console.log(error.toString()))
            

            export const fetchItems = () =>
              (dispatch, getState) => getItems(getState().userId)
              .then(items => dispatch({type: 'SET_ITEMS', items: items}))
            .catch((error) => console.log(error))
            

            export const UserItems = () => {
              const dispatch = useDispatch()

              handleClick = () => {
                const action = fetchItems()
                dispatch(action)
              }

              return ...
            }

            

TP-05

Shopping list- ter

  1. Configure the "thunk" Middleware in index.js
  2. Like TP-04 but with an asynchronous call (http) to load the items list
  3. Bonus: Create an action which applies a VTA rate, when adding a new item to the store

Bravo !

It was the most complicated part

We know how to create components and pass data to them

  • From the parent's component
  • From the LocalState
  • From the store
  • From a grand(n)-parent through the context...

Context

Context API

  • We can pass data to child components (everywhere in the hierarchy).
  • Many librairies are based on it. It was not an official API before react 16.3
  • react-redux works with it, the store is passed to all children.

How to use

Create the Context, and wrap the application with <Context.Provider>


                const Context = React.createContext({color: 'green', user: null})
                export default Context
            

                ...
                return <Context.Provider
                            value={{color: 'purple', user: new User(1, 'admin')}}>
                    <App/>
                </Context.Provider>
            

How to use

Then get the contextwhere it is used with the useContext() hook


            import React, {useContext} from 'react'
            import MyContext from './Context'

            const MyComponent = () => {
                const ctx = useContext(MyContext)
                return <div
                        style={{backgroundColor: ctx.color}}
                        >
                   Some text
                </div>
              }
            }
            

TP-06

Context API

The goal is to pass a color and a user from a component to its 'grand-child'

  1. Wrap a react component into a Context, with a user and a color.
  2. Display the data from the context in the grand child component (Small)

Tests

Jest, Enzyme, React Test Renderer, React Testing Library, ...

The idea is to test presentational components, with their states and props.

Again Hooks were a game changer.

  • Enzyme: Hooks are not yet fully supported
  • React test renderer: No event simulation
  • React testing library: Too complex ?

Render Components


            import React from "react"
            import {shallow} from "enzyme"

            describe('MyComponent', () => {
                it('should work !', () => {
                    const component = shallow(<MyComponent/>)
                })
            })
            

Jest API

Enzyme API

Render Components


            import React from "react"
            import renderer from 'react-test-renderer'

            describe('MyComponent', () => {
                it('should work !', () => {
                    const component = renderer.create(<MyComponent/>)
                })
            })
            

Jest API

React Test Renderer

Test if sub components are loaded


            it('renders App and its sub components', () => {
                const component = shallow(<App/>)
                expect(component.find(LeftMenu).exists())
                    .toBeTruthy()
                expect(component.find(RightMenu).exists())
                    .toBeTruthy()
            })
            

            it('renders App and its sub components', () => {
                const component = renderer.create(<App/>)
                expect(component.root.findByType(LeftMenu)).toBeTruthy()
                expect(component.root.findByType(RightMenu)).toBeTruthy()
            })
            

Snapshots


          it('renders the same', () => {
            const component = shallow(<App/>)
            expect(component).toMatchSnapshot()
          })
          

          it('renders the same', () => {
            const component = renderer.create(<App/>)
            expect(component.toJSON()).toMatchSnapshot()
          })
          

Displayed text


          it('renders a text', () => {
            const component = shallow(<App/>)
            expect(component.text()).toContain('mon texte attendu')
          })
          

          it('renders a text', () => {
            const component = renderer.create(<App/>)
            expect(component.root.props.children)
              .toContain('mon texte attendu')
          })
          

Assert that a method has been called


            const mockOnChange = jest.fn()
            expect(mockOnChange).toBeCalledWith(myParam)
          

Simulate events


            //with an enzyme component
            component.find('button')
                .at(1)
                .simulate('click')
            component.find('input')
                .simulate('change', {target: {value: 'ma valeur'}})
            

Advanced testing - hooks


          export const MyComponent = () => {
              const [id, setId] = useState(0)
              useEffect(() => {
                setId(42)
              }, [])

              return 
{id}
}

Advanced testing - hooks


          const mockSetState = jest.fn()
          jest.mock('react', () => ({
            ...jest.requireActual('react'),
            useState: jest.fn().mockImplementation(
              (s) => [s, mockSetState]
            ),
          }))
          describe('MyComponent', () => {
            it('uses hooks !', () => {
              let component
              act(() => {
                component = renderer.create(<MyComponent />)
              })
            
              expect(component.toJSON()).toMatchSnapshot()
              expect(mockSetState).toHaveBeenCalledWith(42)
            })    
          }
          

Advanced mocking with Jest - functions


            import * as Maths from './maths'

            describe('Maths', () => {
              it('should add', () => {
                Maths.add = jest.fn().mockImplementationOnce(() => 21)

                expect(Maths.add(1,2)).toEqual(21)
              }
            })
            

Advanced mocking with Jest - modules


                import * as Maths from './maths'
                import { calculette } from './calculette'

                jest.mock('./maths')
                describe('calculette', () => {
                    it('should use maths module to performs add42', () => {
                        calculette.add42(11)

                        expect(Maths.add).toHaveBeenCalledWith(42, 11)
                    }
                })
            

Run tests

  • npm test in command line
  • jest runner in Webstorm
  • --coverage for test coverage

TP-07

Tests

The goal is to test the TP-03

  1. Create a .test.js file for each component
  2. StudentDetails: Check the text (Student.NULL or a new Student)
  3. Filter: Verify that the callback is called when the change event is propagated
  4. StudenstTable: Verify the number of rows in the table, given an input. Verify the callback too.
  5. StudentsApp: Verify that the component and its children are displayed. Validate that filteredStudents() returns the correct elements, given an input.
  6. Bonus: Create jest snapshots

Routing

react-router

There are different Routers for managing history of navigation and URL:

  • BrowserRouter : /monsite/page1/partie2
  • HashRouter: /monsite/#/page1/partie2
  • MemoryRouter : /

For BrowserRouter you have to redirect every requests to index.html, but not .css, .js, calls to webservices.

react-router

Define Router in the root level of the application.


            import {BrowserRouter as Router,
                Route} from 'react-router-dom'

            ReactDOM.render(
              <Router>
                  <Route exact path="/" component={Home} />
                  <Route path="/about" render={() => 

About...

} /> <Route path="/admin" component={Admin} /> </Router>, document.getElementById('root') )

Nested routes !


            const Admin = () => (
            <div>
              <h2>Administration</h2>
              <Route path={'/admin/users'} component={UsersAdmin}/>
              <Route path={'/admin/articles'} component={ArticlesAdmin}/>
            </div>
            )
            

react-router

It is possible to generate links automatically (see documentation) doc for thr API


            <Link to="/admin/users">Administration des utilisateurs</Link>
            

react-router

Dynamic urls


            <Route path={'/admin/users/:userId'} component={UserAdmin}/>
            

            const UserAdmin = ({match}) => (
            <div>
                Manage user {match.params.userId}
            </div>
            )
            

match

match contains different data:

  • params: keys values for url params (ex: /user/:id)
  • url: current matched url/li>
  • isExact: true if url has perfectly matched
  • path: pattern of the match

            const Admin = ({match}) => (
                <Link to={`${match.url}/users`}>
                    Users
                </Link>
                <Link to={`${match.url}/articles`}>
                    Articles
                </Link>
            )
            

TP-08

React-router

The goal is to provide an application with 3 "pages"

Then create an application avec 3 pages, one will have an id in the URLs params

  1. Create links to /about and /users
  2. Create routes for /about and /users
  3. Create links to /users/elsa et /users/anna
  4. Create the route to UserDetail component
  5. Write the link to profile pictures (elsa.jpeg and anna.jpeg)

Optimizations

Re render only what is necessary

update

Reconciliation

Virtual DOM is... a memory representation of the components. When a component is rendered, the new V-DOM is compared to the old V-DOM. If there is a difference, the DOM of the browser is (partially) updated.

Too many re-rendering

  • Memoization
  • useCallback()
  • useMemo()

useCallback()


            const MyInput = () => {
                const [value, setValue] = useState('')
                <input value={value}
                    onChange={e => setValue(e.target.value)} />
            }
            

            const MyInput = () => {
                const [value, setValue] = useState('')
                const handleInput = e => setValue(e.target.value)

                <input value={value} onChange={handleInput} />
            }
            

            const MyInput = () => {
                const [value, setValue] = useState('')
                const handleInput = useCallback(e =>
                    setValue(e.target.value), [setValue])

                <input value={value} onChange={handleInput} />
            }
            

useMemo


            const User = ({user}) =>  <div>{user.name}</div>
          

            const MemoizedUser = ({user}) => {
                return  useMemo(() => <User user={user}>, [user.id])
            }
           

            // inside a component
            const memoizedUser = useMemo(() => <User user={user} />,
              [user.id])
            return {memoizedUser}
          

            const MemoizedUser = React.memo(({ user }) =>
              <User user={user} />)
          

TP-09

Optimization

Try different kind of memoization with memo, useMemo and useCallback

Let's do it !

help ? jbcazaux@gmail.com

http://codox.fr/formation-reactjs-es6