SlideShare a Scribd company logo
B
R
Maarten Mulders (@mthmulders)#reactworkshop
T M
a short intro in React and JSX
Starter — Work on your own task management app!
discuss event handling, interacting with REST, state
Main Course — Add behaviours to the app you've created
discuss testing
Dessert — Wrap-up
Maarten Mulders (@mthmulders)#reactworkshop
R JSX
Maarten Mulders (@mthmulders)#reactworkshop
“React is a library for declaratively
building user interfaces using
JavaScript and (optionally) XML.
Maarten Mulders (@mthmulders)#reactworkshop
R
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
Maarten Mulders (@mthmulders)#reactworkshop
W JSX
A syntax extension to JavaScript
real XML, not a string of characters
allows embedded expressions
supports attributes
Can be nested
Automatic XSS prevention
Needs to be transpiled to JavaScript
e.g. React.createElement(...)
Maarten Mulders (@mthmulders)#reactworkshop
E
Elements can be regular DOM elements... (for now, but not for long)
Hej!
const element = <div>Hej!</div>
ReactDOM.render(element, document.getElementById('app'));
1
2
Maarten Mulders (@mthmulders)#reactworkshop
A
Elements can have attributes...
Hej
const element = <div id='foo'>Hej</div>
ReactDOM.render(element, document.getElementById('app'));
1
2
Maarten Mulders (@mthmulders)#reactworkshop
... but they can have different names than HTML attributes:
Hej
const element = <div className='red­text'>Hej</div>
ReactDOM.render(element, document.getElementById('app'));
1
2
Maarten Mulders (@mthmulders)#reactworkshop
... and they can behave differently:
Hej
const style = { color: 'red' };
const element = <div style={ style }>Hej</div>
ReactDOM.render(element, document.getElementById('app'));
1
2
3
Maarten Mulders (@mthmulders)#reactworkshop
C
“Reusable building blocks for composing
user interfaces.
Maarten Mulders (@mthmulders)#reactworkshop
S (F )
Function that takes props and returns a React element.
Hi, Jfokus!
const Greeter = (props) => <div>Hi, { props.name }!</div>
ReactDOM.render(<Greeter name='Jfokus' />, document.getElementById('app'));
1
2
Maarten Mulders (@mthmulders)#reactworkshop
S
Class that can store state; render() method returns a React element.
Hi, Jfokus!
class Greeter extends React.Component {
   constructor(props) {
       super(props)
       // Usually you don't copy props into state
       this.state = { name: props.name };
   }
   render() {
       return <div>Hi, { this.state.name }!</div>;
   }
}
ReactDOM.render(<Greeter name='Jfokus' />, document.getElementById('app'));
1
2
3
4
5
6
7
8
9
10
11
Maarten Mulders (@mthmulders)#reactworkshop
S R N
Values must have a single root node
x
y
const element = <><div>x</div><div>y</div></>
ReactDOM.render(element, document.getElementById('app'));
1
2
Maarten Mulders (@mthmulders)#reactworkshop
T R
Compose applications from (re-usable) components
consistent
verifiable & testable
clear interface
⟶ single responsibility principle
Maarten Mulders (@mthmulders)#reactworkshop
L D T
Start with a mockup of what you want to build
Inbox
@Work
@Home
@Supermarket
Learn React
Build my first React app
Add new task Add
Task Learn React
Due date April 13, 2020
Completed yes
List Inbox
Group exercise: identify component hierarchy
Maarten Mulders (@mthmulders)#reactworkshop
G T D S
Maarten Mulders (@mthmulders)#reactworkshop
L D T
Start with a mockup of what you want to build
Inbox
@Work
@Home
@Supermarket
Learn React
Build my first React app
Add new task Add
Task Learn React
Due date April 13, 2020
Completed yes
List Inbox
1. Identify component hierarchy
2. Create skeleton of component hierarchy
Use background or border colors to distinguish componentsMaarten Mulders (@mthmulders)#reactworkshop
S JSX
Maarten Mulders (@mthmulders)#reactworkshop
E JSX
42
const answerToQuestionOfLife = 42;
const askQuestionOfLife = () => answerToQuestionOfLife;
const App = () => <div>{ askQuestionOfLife() }</div>;
ReactDOM.render(<App/>, document.getElementById('app'));
1
2
3
4
5
6
Maarten Mulders (@mthmulders)#reactworkshop
C JSX
Clapping hands...
const ClapHands = () => <span>Clapping hands...</span>;
const DryTears = () => <span>Drying tears...</span>;
const App = ({ isHappy }) => <div>{ isHappy ? <ClapHands /> : <DryTears /> }</div>;
ReactDOM.render(<App isHappy={ true } />, document.getElementById('app'));
1
2
3
4
5
Maarten Mulders (@mthmulders)#reactworkshop
C JSX (2)
ASML
PHIA
RDSA
const Ticker = ({ symbol }) => <div>{ symbol }</div>;
const TickerList = ({ symbols }) => symbols.map(
  (symbol) => <Ticker symbol={ symbol } />
);
const symbols = ['ASML', 'PHIA', 'RDSA'];
ReactDOM.render(<TickerList symbols={ symbols } />, document.getElementById('app'));
1
2
3
4
5
6
7
8
Maarten Mulders (@mthmulders)#reactworkshop
M
Maarten Mulders (@mthmulders)#reactworkshop
A
So far, we've declared components and elements.
Babel or tsc transpiles JSX into React.createElement(...)
invocations:
<Greeter name={ 'world' }
/** transpiles into
React.createElement(Greeter, { name: 'world' }, null)
Maarten Mulders (@mthmulders)#reactworkshop
A
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.
Maarten Mulders (@mthmulders)#reactworkshop
R
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.
Maarten Mulders (@mthmulders)#reactworkshop
1 E
Hej, Jfokus
const SwedishGreeter = ({ name }) => <div>Hej, { name }</div>
const DutchGreeter = ({ name }) => <div>Hallo, { name }</div>;
const EnglishGreeter = ({ name }) => <div>Hello, { name }</div>;
const App = ({ language, name }) => {
 switch(language) {
   case 'se': return <SwedishGreeter name={ name } />
   case 'nl': return <DutchGreeter name={ name } />
   case 'en':
   default  : return <EnglishGreeter name={ name } />
 }
};
ReactDOM.render(<App name='Jfokus' language='se' />, document.getElementById('app'));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Maarten Mulders (@mthmulders)#reactworkshop
2 T KEY
ASML
PHIA
RDSA
const Ticker = ({ symbol }) => <div>{ symbol }</div>;
const TickerList = ({ symbols }) => symbols.map(
  (symbol) => <Ticker key={ symbol } symbol={ symbol } />
);
const symbols = ['ASML', 'PHIA', 'RDSA'];
ReactDOM.render(<TickerList symbols={ symbols } />,
document.getElementById('app'));
1
2
3
4
5
6
7
8
9
Maarten Mulders (@mthmulders)#reactworkshop
L D T
Start with a mockup of what you want to build
Inbox
@Work
@Home
@Supermarket
Learn React
Build my first React app
Add new task Add
Task Learn React
Due date April 13, 2020
Completed yes
List Inbox
1. Identify component hierarchy
2. Create skeleton of component hierarchy
Use background or border colors to distinguish componentsMaarten Mulders (@mthmulders)#reactworkshop
A B
1. Event handling
2. Storing data
1. Temporarily (in your app)
2. Permanent (in your browser)
3. Getting data over HTTP
Maarten Mulders (@mthmulders)#reactworkshop
R E
Similar to DOM event handling, but
1. event names are different: onClick vs onclick.
2. event handles are always functions, never strings.
3. event handlers receive an synthetic event - browser-agnostic!
Maarten Mulders (@mthmulders)#reactworkshop
B C C
Clickme!
const Greeter = ({ name }) => {
    const callback = () => alert(name)
    return <button onClick={ callback }>Click me!</button>
}
ReactDOM.render(<Greeter name='Jfokus' />, document.getElementById('app'));
1
2
3
4
5
6
Maarten Mulders (@mthmulders)#reactworkshop
K
1. Inside a component
2. Shared in your app
Maarten Mulders (@mthmulders)#reactworkshop
L S C
Counter: 0
+   ‑
const Counter = () => {
   const [ counter, setCounter ] = React.useState(0); // Look, a Hook!
   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
Maarten Mulders (@mthmulders)#reactworkshop
U U I
Greetme!
const Greeter = () => {
   const [ name, setName ] = React.useState('');
   const updateName = (e) => setName(e.target.value);
   const callback = () => window.alert('Hi ' + name + '!');
   return <div><input type='text' onChange={ updateName } ></input><br />
               <button onClick={ callback }>Greet me!</button></div>
}
ReactDOM.render(<Greeter />, document.getElementById('app'));
1
2
3
4
5
6
7
8
9
10
Maarten Mulders (@mthmulders)#reactworkshop
S S A
Share state between components by passing props. But what if they
have a few components in between?
          Producer                    In Between 1                  In Between 2                    Consumer           
“Seperation of concerns, anyone?
Maarten Mulders (@mthmulders)#reactworkshop
C
Welcome, Jfokus
const UserContext = React.createContext();
const UserDisplay = () => {
   const user = React.useContext(UserContext);
   return <div>Welcome, { user ? user.name : 'guest' }</div>;
};
const InBetween2 = () => <UserDisplay />
const InBetween1 = () => <InBetween2 />
const App = ({ name }) => {
   const user = name ? { name } : undefined;
   return (<UserContext.Provider value={ user }>
               <InBetween1 />
           </UserContext.Provider>);
}
ReactDOM.render(<App name='Jfokus' />, document.getElementById('app'));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Maarten Mulders (@mthmulders)#reactworkshop
U C
Current theme: light.
Toggle!
const ThemeContext = React.createContext();
const ThemeDisplay = () => {
   const context = React.useContext(ThemeContext);
   return <div>Current theme: { context.theme }.</div>
};
const ThemeToggle = () => {
   const context = React.useContext(ThemeContext);
   return <button onClick={ context.toggle }>Toggle!</button>
};
const App = () => {
   const [ theme, setTheme ] = React.useState('light');
   const toggle = () => setTheme(theme === 'dark' ? 'light' : 'dark');
   return <ThemeContext.Provider value={ { theme, toggle } } >
              <ThemeDisplay />
/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 
Maarten Mulders (@mthmulders)#reactworkshop
Alternatively, use .contextType for consuming a Context
⟶ Only works for classes!
class WhateverDisplay extends React.Component {
render() {
return <div className={ this.context.theme }> div>;
}
}
WhateverDisplay.contextType = ThemeContext;
Maarten Mulders (@mthmulders)#reactworkshop
G
1. What state do we have?
2. Where does it live?
Maarten Mulders (@mthmulders)#reactworkshop
S
, anyone?
limited storage (~ 4096 bytes each, max 50 per domain)
can be insecure (sent to web server)
Maarten Mulders (@mthmulders)#reactworkshop
L S S S
Part of the
Stores and retrieves string values
Serialise objects with JSON.stringify()
Deserialise with JSON.parse()
Aren't sent to a web server
Persistent
during browser session with sessionStorage
over browser shutdowns with localStorage
Web Storage API
Maarten Mulders (@mthmulders)#reactworkshop
U S S
Counter: 0
+ ‑
const initialValue = Number(sessionStorage.getItem('counter')) || 0;
const App = () => {
   const [ counter, setCounter ] = React.useState(initialValue);
   const updateCounter = (value) => {
       setCounter(Number(value));
       sessionStorage.setItem('counter', value);
   }
   const increase = () => updateCounter(counter + 1);
   const decrease = () => updateCounter(counter ­ 1);
   return <div>Counter: { counter }<br />
               <button onClick={ increase }>   +   </button>
               <button onClick={ decrease }>   ­   </button>
          </div>
};
/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 
Maarten Mulders (@mthmulders)#reactworkshop
F HTTP
Prerequisites:
1. A JavaScript function to fetch some data
2. A component to show the fetched data
Maarten Mulders (@mthmulders)#reactworkshop
1 A 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: hqd7fvgovgs2jyoosjgryaylcy.apigateway.eu frankfurt-1.oci.customer oci.com'
+ '/v1/joke';
const getJoke = () {
return fetch(url)
.then(checkStatus)
.then(parseJson)
.then(response response.joke);
};
Maarten Mulders (@mthmulders)#reactworkshop
2 A
My friend didn't understand cloning. That makes two of us.
const RandomJoke = () => {
   const [ { joke, loading }, setState ] = React.useState({ loading: true });
   const fetchRandomJoke = async () => {
       // Does the actual API call to Oracle Cloud function,
       // see code on previous slide.
       const joke = await getJoke();
       setState({ loading: false, joke });
   }
   React.useEffect(() => {
       fetchRandomJoke()
   }, [ /* intentionally left empty */ ]);
   if (loading) return <div>Loading...</div>
   return <div>{ joke }</div>;
};
ReactDOM.render(<RandomJoke />, document.getElementById('app'));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Maarten Mulders (@mthmulders)#reactworkshop
W
If the API will be available at the same domain as the web app, use a
proxy.
Add to package.json (assuming you use )Create React App
"proxy": "https: gtdapp.mulders.it"
Maarten Mulders (@mthmulders)#reactworkshop
L D T
Start with a mockup of what you want to build
Inbox
@Work
@Home
@Supermarket
Learn React
Build my first React app
Add new task Add
Task Learn React
Due date April 13, 2020
Completed yes
List Inbox
1. Identify component hierarchy
2. Create skeleton of component hierarchy
Use background or border colors to distinguish componentsMaarten Mulders (@mthmulders)#reactworkshop
W GTD REST API
1. Register an account:
2. Install the client lib:
3. Update package.json:
4. Restart NPM:
curl d 'username= &password= '
https: gtdapp.mulders.it/public/users/register
npm i gtd lib
"proxy": "https: gtdapp.mulders.it"
Maarten Mulders (@mthmulders)#reactworkshop
V O S
Maarten Mulders (@mthmulders)#reactworkshop
T
For unit testing, Jest and Enzyme are very powerful solution. Install
them with
Create a file src/setupTests.js with
npm i -D enzyme enzyme adapter react-16 react test renderer
import { conf gure } from 'enzyme';
import Adapter from 'enzyme adapter react-16';
conf gure({ adapter: new Adapter() });
Maarten Mulders (@mthmulders)#reactworkshop
W
Tests go in:
Files with .js suffix in __tests__ folders.
Files with .test.js suffix.
Files with .spec.js suffix.
Run them with npm test.
Maarten Mulders (@mthmulders)#reactworkshop
T : S R
⚠ Does not render any child components
import React from 'react';
import { shallow } from 'enzyme';
import Greeter from ' /Greeter';
describe('<Greeter ', () {
it('renders a friendly greeting', () {
const wrapper = shallow(<Greeter name='folks' );
expect(wrapper.text()).toBe('Hi folks!');
});
});
Maarten Mulders (@mthmulders)#reactworkshop
T E H
describe('<MyButton ', () {
it('handles click events', () {
const onButtonClick = jest.mock();
const wrapper = shallow(<Foo onButtonClick={ onButtonClick } );
wrapper.f nd('button').simulate('click');
expect(onButtonClick).toBeCalled();
});
});
Maarten Mulders (@mthmulders)#reactworkshop
F DOM R
⚠ Needs an environment that "looks like" a browser, e.g. .
Otherwise, add ­­env=node to the test script in package.json.
import React from 'react';
import { mount } from 'enzyme';
import Greeter from ' /Greeter';
describe('<Greeter ', () {
it('renders a friendly greeting', () {
const wrapper = mount(<Greeter name='Folks' );
expect(wrapper.text()).toBe('Hi Folks!');
});
});
jsdom
Maarten Mulders (@mthmulders)#reactworkshop
W -
Start with a mockup of what you want to build
Inbox
@Work
@Home
@Supermarket
Learn React
Build my first React app
Add new task Add
Task Learn React
Due date April 13, 2020
Completed yes
List Inbox
1. Identify component hierarchy
2. Create skeleton of component hierarchy
Use background or border colors to distinguish componentsMaarten Mulders (@mthmulders)#reactworkshop

More Related Content

PDF
React in 45 Minutes (Jfokus)
PDF
Hibernate Mapping
PDF
React in 50 Minutes (DevNexus)
PDF
React in 50 Minutes (OpenValue)
PDF
React in 40 minutes (JCON)
PDF
React in 40 minutes (Voxxed Days Romania)
PDF
React in 50 minutes (Bucharest Software Craftsmanship Community)
PDF
React in 50 Minutes (JNation)
React in 45 Minutes (Jfokus)
Hibernate Mapping
React in 50 Minutes (DevNexus)
React in 50 Minutes (OpenValue)
React in 40 minutes (JCON)
React in 40 minutes (Voxxed Days Romania)
React in 50 minutes (Bucharest Software Craftsmanship Community)
React in 50 Minutes (JNation)

Similar to Building web applications with React (Jfokus) (20)

PDF
How To Integrate Native Android App With React Native.
PDF
Refactoring
PDF
Simple React Todo List
PPTX
React js - The Core Concepts
PDF
React Native +Redux + ES6 (Updated)
PPTX
React/Redux
PPTX
Vývojařská Plzeň - React
PDF
PPTX
React Native.pptx (2)
PDF
Materi Modern React Redux Power Point.pdf
PDF
Having Fun Building Web Applications (Day 2 slides)
PDF
Advanced Interfaces and Repositories in Laravel
PDF
Mobile Software Engineering Crash Course - C04 Android Cont.
PDF
React Best Practices All Developers Should Follow in 2024.pdf
PDF
Save time by applying clean code principles
PDF
Reactive programming in clojure script using reactjs wrappers
PDF
Time to React!
PPTX
React - An Introduction
PPTX
Crafted Design - GeeCON 2014
PDF
Resources and relationships at front-end
How To Integrate Native Android App With React Native.
Refactoring
Simple React Todo List
React js - The Core Concepts
React Native +Redux + ES6 (Updated)
React/Redux
Vývojařská Plzeň - React
React Native.pptx (2)
Materi Modern React Redux Power Point.pdf
Having Fun Building Web Applications (Day 2 slides)
Advanced Interfaces and Repositories in Laravel
Mobile Software Engineering Crash Course - C04 Android Cont.
React Best Practices All Developers Should Follow in 2024.pdf
Save time by applying clean code principles
Reactive programming in clojure script using reactjs wrappers
Time to React!
React - An Introduction
Crafted Design - GeeCON 2014
Resources and relationships at front-end
Ad

More from Maarten Mulders (20)

PDF
What's cooking in Maven? (Devoxx FR)
PDF
Making Maven Marvellous (Devnexus)
PDF
Making Maven Marvellous (Java.il)
PDF
Making Maven Marvellous (JavaZone)
PDF
Dapr: Dinosaur or Developer's Dream? (v1)
PDF
Dapr: Dinosaur or Developer Dream? (J-Fall)
PDF
SSL/TLS for Mortals (Devoxx UK)
PDF
SSL/TLS for Mortals (JavaLand)
PDF
Making Maven Marvellous (J-Fall)
PDF
Building a DSL with GraalVM (Oracle Groundbreaker APAC Virtual Tour)
PDF
SSL/TLS for Mortals (Oracle Groundbreaker EMEA Virtual Tour)
PDF
SSL/TLS for Mortals (UtrechtJUG)
PDF
Building a DSL with GraalVM (javaBin online)
PDF
SSL/TLS for Mortals (Lockdown Lecture)
PDF
SSL/TLS for Mortals (Devoxx)
PDF
Building a DSL with GraalVM (CodeOne)
PDF
Building a DSL with GraalVM (Full Stack Antwerpen)
PDF
Building a DSL with GraalVM (Devoxx PL)
PDF
Building a DSL with GraalVM (VoxxedDays Luxembourg)
PDF
Mastering Microservices with Kong (DevoxxUK 2019)
What's cooking in Maven? (Devoxx FR)
Making Maven Marvellous (Devnexus)
Making Maven Marvellous (Java.il)
Making Maven Marvellous (JavaZone)
Dapr: Dinosaur or Developer's Dream? (v1)
Dapr: Dinosaur or Developer Dream? (J-Fall)
SSL/TLS for Mortals (Devoxx UK)
SSL/TLS for Mortals (JavaLand)
Making Maven Marvellous (J-Fall)
Building a DSL with GraalVM (Oracle Groundbreaker APAC Virtual Tour)
SSL/TLS for Mortals (Oracle Groundbreaker EMEA Virtual Tour)
SSL/TLS for Mortals (UtrechtJUG)
Building a DSL with GraalVM (javaBin online)
SSL/TLS for Mortals (Lockdown Lecture)
SSL/TLS for Mortals (Devoxx)
Building a DSL with GraalVM (CodeOne)
Building a DSL with GraalVM (Full Stack Antwerpen)
Building a DSL with GraalVM (Devoxx PL)
Building a DSL with GraalVM (VoxxedDays Luxembourg)
Mastering Microservices with Kong (DevoxxUK 2019)
Ad

Recently uploaded (20)

PPTX
Operating system designcfffgfgggggggvggggggggg
PDF
Digital Systems & Binary Numbers (comprehensive )
PDF
Which alternative to Crystal Reports is best for small or large businesses.pdf
PDF
EN-Survey-Report-SAP-LeanIX-EA-Insights-2025.pdf
PPTX
VVF-Customer-Presentation2025-Ver1.9.pptx
PDF
Wondershare Filmora 15 Crack With Activation Key [2025
PDF
wealthsignaloriginal-com-DS-text-... (1).pdf
PPTX
ai tools demonstartion for schools and inter college
PDF
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
PDF
medical staffing services at VALiNTRY
PDF
How to Migrate SBCGlobal Email to Yahoo Easily
PDF
Design an Analysis of Algorithms I-SECS-1021-03
PPT
Introduction Database Management System for Course Database
PDF
Adobe Premiere Pro 2025 (v24.5.0.057) Crack free
PPTX
Computer Software and OS of computer science of grade 11.pptx
PPTX
L1 - Introduction to python Backend.pptx
PDF
PTS Company Brochure 2025 (1).pdf.......
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 41
PPTX
Embracing Complexity in Serverless! GOTO Serverless Bengaluru
PDF
top salesforce developer skills in 2025.pdf
Operating system designcfffgfgggggggvggggggggg
Digital Systems & Binary Numbers (comprehensive )
Which alternative to Crystal Reports is best for small or large businesses.pdf
EN-Survey-Report-SAP-LeanIX-EA-Insights-2025.pdf
VVF-Customer-Presentation2025-Ver1.9.pptx
Wondershare Filmora 15 Crack With Activation Key [2025
wealthsignaloriginal-com-DS-text-... (1).pdf
ai tools demonstartion for schools and inter college
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
medical staffing services at VALiNTRY
How to Migrate SBCGlobal Email to Yahoo Easily
Design an Analysis of Algorithms I-SECS-1021-03
Introduction Database Management System for Course Database
Adobe Premiere Pro 2025 (v24.5.0.057) Crack free
Computer Software and OS of computer science of grade 11.pptx
L1 - Introduction to python Backend.pptx
PTS Company Brochure 2025 (1).pdf.......
Internet Downloader Manager (IDM) Crack 6.42 Build 41
Embracing Complexity in Serverless! GOTO Serverless Bengaluru
top salesforce developer skills in 2025.pdf

Building web applications with React (Jfokus)