Upgrade to Pro — share decks privately, control downloads, hide ads and more …

React in 50 minutes (DubJUG)

React in 50 minutes (DubJUG)

You’ve heard about React, the JavaScript library for building web applications? And you’re wondering how to use it beyond the “Hello, World” stage? Then this talk is for you! Join me in an action-packed session full of live code examples where we’ll discover how you can use React to build the hottest web applications while keeping your head cool.

When you leave the room, you’ll know enough to build real-world web applications!

Maarten Mulders

March 01, 2023
Tweet

More Decks by Maarten Mulders

Other Decks in Programming

Transcript

  1. REACT... “ React is a library for declaratively building user

    interfaces using JavaScript and (optionally) XML. #reactin50mins Maarten Mulders (@mthmulders)
  2. REACT VS. OTHERS No two-way data binding No templating language

    Just user interface (no routing, no HTTP client) Plain JavaScript (or add JSX, TypeScript, ...) Virtual DOM vs. actual DOM #reactin50mins Maarten Mulders (@mthmulders)
  3. CLASSES class Amount { constructor(currency, value) { this.currency = currency;

    this.value = value; } getCurrency() { return this.currency; } } const text = new Amount('EUR', 15).getCurrency(); document.getElementById('app').innerText = text; 1 2 3 4 5 6 7 8 9 10 11 12 13 EUR #reactin50mins Maarten Mulders (@mthmulders)
  4. FUNCTIONS function isEven(number) { return number % 2 == 0;

    } const text = isEven(42) document.getElementById('app').innerText = text; 1 2 3 4 5 6 true #reactin50mins Maarten Mulders (@mthmulders)
  5. ARROW FUNCTIONS const isEven = (number) => { return number

    % 2 == 0; } const text = isEven(42) document.getElementById('app').innerText = text; 1 2 3 4 5 6 true #reactin50mins Maarten Mulders (@mthmulders)
  6. OBJECT DECOMPOSITION const person = { name : 'Jane Doe',

    age: 42, occupancy: 'JavaScript dev' }; const { name, age } = person; const text = age document.getElementById('app').innerText = text; 1 2 3 4 5 42 #reactin50mins Maarten Mulders (@mthmulders)
  7. ARRAY DECOMPOSITION const numbers = [ 'one', 'two', 'three' ];

    const [ first, second ] = numbers; const text = second document.getElementById('app').innerText = text; 1 2 3 4 5 two #reactin50mins Maarten Mulders (@mthmulders)
  8. OBJECT SHORTHAND NOTATION const name = 'Jane Doe'; const age

    = 42; const person = { name, age }; const text = JSON.stringify(person) document.getElementById('app').innerText = text; 1 2 3 4 5 6 {"name":"Jane Doe","age":42} #reactin50mins Maarten Mulders (@mthmulders)
  9. STRING INTERPOLATION class Amount { constructor(currency, value) { this.currency =

    currency; this.value = value; } toString() { return `${this.currency} - ${this.value}`; } } const text = new Amount('EUR', 150).toString(); document.getElementById('app').innerText = text; 1 2 3 4 5 6 7 8 9 10 11 12 13 EUR - 150 #reactin50mins Maarten Mulders (@mthmulders)
  10. WHAT IS JSX? A syntax extension to JavaScript real XML,

    not a string of characters allows embedded expressions supports attributes Can be nested Automatic XSS prevention But it has its limitations Needs to be transpiled to JavaScript e.g. React.createElement(...) #reactin50mins Maarten Mulders (@mthmulders)
  11. ELEMENTS Elements can be regular DOM elements... (for now, but

    not for long) const element = <div>Hello, world</div> ReactDOM.render(element, document.getElementById('app')); 1 2 Hello, world #reactin50mins Maarten Mulders (@mthmulders)
  12. ATTRIBUTES Elements can have attributes, but they can have different

    names than HTML attributes: const element = <div className='red-text'>I'm blue</div> ReactDOM.render(element, document.getElementById('app')); 1 2 I'm blue #reactin50mins Maarten Mulders (@mthmulders)
  13. ... and they can behave differently: const style = {

    color: 'red', fontWeight: 'bold' }; const element = <div style={ style }>I'm blue</div> ReactDOM.render(element, document.getElementById('app')); 1 2 3 I'm blue #reactin50mins Maarten Mulders (@mthmulders)
  14. SINGLE ROOT NODE Values must have a single root node

    (or an array) const element = [<div>x</div>,<div>y</div>] ReactDOM.render(element, document.getElementById('app')); 1 2 3 x y #reactin50mins Maarten Mulders (@mthmulders)
  15. COMPONENTS Function that returns a React element. const Greeter =

    (props) => <div>Hello, world!</div> ReactDOM.render(<Greeter />, document.getElementById('app')); 1 2 Hello, world! #reactin50mins Maarten Mulders (@mthmulders)
  16. EXPRESSIONS IN JSX const answerToQuestionOfLife = 40 + 2; const

    askQuestionOfLife = () => answerToQuestionOfLife; const Example = () => <div> The answer to the ultimate question of life, universe and everything: <strong>{ askQuestionOfLife() }</strong> </div>; ReactDOM.render(<Example />, document.getElementById('app')); 1 2 3 4 5 6 7 8 The answer to the ultimate question of life, universe and everything: 42 #reactin50mins Maarten Mulders (@mthmulders)
  17. OR USE YOUR PROPS / ARGUMENTS! const Greeter = (props)

    => <div>Hello { props.name }!</div> ReactDOM.render(<Greeter name='Dublin' />, document.getElementById('app')); 1 2 Hello Dublin! #reactin50mins Maarten Mulders (@mthmulders)
  18. OR USE YOUR PROPS / ARGUMENTS! Alternatively, using object decomposition:

    const Greeter = ({ name }) => <div>Hello { name }!</div> ReactDOM.render(<Greeter name='Dublin' />, document.getElementById('app')); 1 2 Hello Dublin! #reactin50mins Maarten Mulders (@mthmulders)
  19. CONTROL STATEMENTS INSIDE JSX const ClapHands = () => <span>Clapping

    hands...</span>; const DryTears = () => <span>Drying tears...</span>; const ShowEmotion = ({ happy }) => happy ? <ClapHands /> : <DryTears />; ReactDOM.render(<ShowEmotion happy={ true } />, document.getElementById('app')); 1 2 3 4 5 6 7 Clapping hands... #reactin50mins Maarten Mulders (@mthmulders)
  20. CONTROL STATEMENTS INSIDE JSX (2) const Ticker = ({ symbol

    }) => <div>{ symbol }</div>; const TickerList = ({ symbols }) => symbols.map( (symbol) => <Ticker symbol={ symbol } /> ); const symbols = ['HEIA', 'PHIA']; ReactDOM.render(<TickerList symbols={ symbols } />, document.getElementById('app')); 1 2 3 4 5 6 7 8 9 HEIA PHIA #reactin50mins Maarten Mulders (@mthmulders)
  21. AT BUILD TIME So far, we've written components and wired

    them together. Babel or tsc transpiles them to React.createElement(...) invocations: <Greeter name={ 'World' } /> /** transpiles into */ React.createElement(Greeter, { name: 'World' }, null) #reactin50mins Maarten Mulders (@mthmulders)
  22. AT RUN TIME The React.createElement invocations form a tree of

    components. React maintains a virtual DOM based on your component tree. The virtual DOM is compared to the actual DOM. Only necessary changes are executed. #reactin50mins Maarten Mulders (@mthmulders)
  23. RECONCILIATION React syncs the virtual and the actual DOM based

    on two assumptions: 1. If two elements are of different type, the (sub) tree will be different. 2. The key prop identifies child elements over re-renders. This tells React what makes two elements "the same". #reactin50mins Maarten Mulders (@mthmulders)
  24. 1. ELEMENTS OF DIFFERENT TYPE const DutchGreeter = ({ name

    }) => <div> 🇳🇱 Hallo, { name }!</div>; const EnglishGreeter = ({ name }) => <div> 🇬🇧 Hello, { name }!</div>; const IrishGreeter = ({ name }) => <div> 🇮🇪 Dhuit, { name }!</div>; const App = ({ lang, name }) => { switch(lang) { case 'ie': return <IrishGreeter name={ name } /> case 'nl': return <DutchGreeter name={ name } /> case 'en': default : return <EnglishGreeter name={ name } /> } }; ReactDOM.render(<App name='Dublin' lang='nl' />, document.getElementById('app')); 1 2 3 4 5 6 7 8 9 10 11 12 13 🇳🇱 Hallo, Dublin! #reactin50mins Maarten Mulders (@mthmulders)
  25. 2. THE key PROP const randomPrice = () => 100

    + Math.floor(Math.random() * 900) const Ticker = ({ symbol }) => <span>{ symbol }: { randomPrice() } &mdash; </span>; const symbols = ['HEIA', 'PHIA', 'ASML', 'KLMR'].map( (symbol) => <Ticker key={ symbol } symbol={ symbol } /> ); ReactDOM.render(<Shuffle children={ symbols } />, document.getElementById('app')); 1 2 3 4 5 6 7 8 9 #reactin50mins Maarten Mulders (@mthmulders)
  26. YOUR ENVIRONMENT 1. Keeping state 2. Reacting to events 3.

    Fetching data over HTTP 4. Storing data #reactin50mins Maarten Mulders (@mthmulders)
  27. LOCAL STATE INSIDE A COMPONENT const Counter = () =>

    { const [ counter, setCounter ] = React.useState(0); return <div>Counter: { counter }</div> } ReactDOM.render(<Counter />, document.getElementById('app')); 1 2 3 4 5 6 7 Counter: 0 #reactin50mins Maarten Mulders (@mthmulders)
  28. REACTING TO EVENTS Similar to DOM event handling, but 1.

    event names are different: onClick vs onclick. 2. event handlers are always functions, never strings. 3. event handlers are bound to a component, should not live globally. 4. event handlers receive an synthetic event - browser-agnostic! #reactin50mins Maarten Mulders (@mthmulders)
  29. CLICK CALLBACKS const Counter = () => { const [

    counter, setCounter ] = React.useState(0); const increase = () => setCounter(counter + 1); const decrease = () => setCounter(counter - 1); return <div>Counter: { counter }<br /> <button onClick={ increase }> + </button> &nbsp; <button onClick={ decrease }> - </button> </div> } ReactDOM.render(<Counter />, document.getElementById('app')); 1 2 3 4 5 6 7 8 9 10 11 12 Counter: 0 + - #reactin50mins Maarten Mulders (@mthmulders)
  30. CHANGE CALLBACKS const Greeter = () => { const [

    name, setName ] = React.useState(''); const updateName = (e) => setName(e.target.value); const callback = () => alert(`Hi ${name}`); return <div><input type='text' onChange={ updateName } ></input><br /> <button onClick={ callback }>Greet { name }!</button></div> } ReactDOM.render(<Greeter />, document.getElementById('app')); 1 2 3 4 5 6 7 8 9 10 Greet ! #reactin50mins Maarten Mulders (@mthmulders)
  31. FETCHING DATA OVER HTTP What we need: 1. A bit

    of Plain Old JavaScript to fetch some data 2. A component to show the fetched data #reactin50mins Maarten Mulders (@mthmulders)
  32. 1. A DATA RETRIEVAL API const url = 'https://opendata.rdw.nl/resource/m9d7-ebf2.json'; const

    getCar = (licenseNumber) => { return fetch(url + '?kenteken=' + licenseNumber) }; #reactin50mins Maarten Mulders (@mthmulders)
  33. 1. A DATA RETRIEVAL API const checkStatus = (response) =>

    { if (response.status >= 200 && response.status < 300) { return Promise.resolve(response); } else { return Promise.reject(`HTTP error ${response.status}: ${response.statusText}`); } }; const url = 'https://opendata.rdw.nl/resource/m9d7-ebf2.json'; const getCar = (licenseNumber) => { return fetch(url + '?kenteken=' + licenseNumber) .then(checkStatus) }; #reactin50mins Maarten Mulders (@mthmulders)
  34. 1. A DATA RETRIEVAL API const checkStatus = (response) =>

    { if (response.status >= 200 && response.status < 300) { return Promise.resolve(response); } else { return Promise.reject(`HTTP error ${response.status}: ${response.statusText}`); } }; const parseJson = (response) => response.json(); const url = 'https://opendata.rdw.nl/resource/m9d7-ebf2.json'; const getCar = (licenseNumber) => { return fetch(url + '?kenteken=' + licenseNumber) .then(checkStatus) .then(parseJson) }; #reactin50mins Maarten Mulders (@mthmulders)
  35. 1. A DATA RETRIEVAL API const checkStatus = (response) =>

    { if (response.status >= 200 && response.status < 300) { return Promise.resolve(response); } else { return Promise.reject(`HTTP error ${response.status}: ${response.statusText}`); } }; const parseJson = (response) => response.json(); const url = 'https://opendata.rdw.nl/resource/m9d7-ebf2.json'; const getCar = (licenseNumber) => { return fetch(url + '?kenteken=' + licenseNumber) .then(checkStatus) .then(parseJson) .then(response => `${response[0].merk} ${response[0].handelsbenaming}`); }; #reactin50mins Maarten Mulders (@mthmulders)
  36. 2. A COMPONENT TO SHOW THE FETCHED DATA const ShowCar

    = () => { const [ { car, loading }, setState ] = React.useState({ loading: true }); const fetchCarDetails = async (licenseNumber) => { const car = await getCar(licenseNumber); setState({loading: false, car}); } React.useEffect(() => { fetchCarDetails('33JFX4'); }, [ ]); return loading ? <div>Loading...</div> : <div>{ car }</div>; }; ReactDOM.render(<ShowCar />, document.getElementById('app')); 1 2 3 4 5 6 7 8 9 10 11 12 13 OPEL ZAFIRA #reactin50mins Maarten Mulders (@mthmulders)
  37. STORING DATA IN YOUR BROWSER Local Storage & Session Storage

    Part of the Stores and retrieves string values Serialise objects with JSON.stringify() Deserialise with JSON.parse() Persistent during browser session with sessionStorage over browser shutdowns with localStorage Web Storage API #reactin50mins Maarten Mulders (@mthmulders)
  38. DEBUGGING Install the React Developer Tools for your browser of

    choice. #reactin50mins Maarten Mulders (@mthmulders)
  39. TESTING YOUR COMPONENTS Use Jest (test platform & library) and

    (React) Testing Library (testing utilities) Render a React component in a unit test Make assertions about its output and behaviour #reactin50mins Maarten Mulders (@mthmulders)
  40. TESTING A SIMPLE COMPONENT import { render, screen } from

    '@testing-library/react' describe('<Greeter />', () => { it('should render text', () => { render(<Greeter name='Dublin' />); expect(screen.getByText(/hello, Dublin/i)).toBeVisible(); }); }); #reactin50mins Maarten Mulders (@mthmulders)
  41. TESTING BEHAVIOUR OF A COMPONENT import { fireEvent, render, screen

    } from '@testing-library/react' describe('<AwesomeButton />', () => { it('should invoke action on click', () => { const callback = jest.mock(); render(<AwesomeButton action={ callback } />); fireEvent.click(screen.getByRole('link')); expect(callback).toHaveBeenCalled(); }); }); #reactin50mins Maarten Mulders (@mthmulders)
  42. DEVELOPING & SHIPPING AN APP tl;dr: use (CRA) Uses Webpack,

    Babel, ESLint and a dozen of other tools Tested to work together Live-reloading of changed code Source maps for easy debugging Have an ready-to-go app in one command Create React App npx create-react-app my-next-killer-app // or npm i -g create-react-app create-react-app my-next-killer-app #reactin50mins Maarten Mulders (@mthmulders)
  43. USING CRA npm run start to start developing npm run

    build to create a production build npm run test to run unit tests #reactin50mins Maarten Mulders (@mthmulders)
  44. TAKE AWAYS Use Create React App Think in (small) components

    Think declaratively #reactin50mins Maarten Mulders (@mthmulders)
  45. THANK YOU! REFERENCES & MORE INFORMATION Create React App Philippe

    De Ryck on "Building Secure React Applications" @mthmulders #reactin50mins Maarten Mulders (@mthmulders)