title image - https://unsplash.com/photos/tjX_sniNzgQ
Contact Info Thanks:
timekillers:
title image - https://unsplash.com/photos/tjX_sniNzgQ
Contact Info Thanks:
timekillers:
intro & setup: 0:00 - 0:?? [timing]
git, node, npm, & repo
If you cloned before yesterday - pull latest!
If not set up, do this while I talk! (point out url)
If lots didn't do it:
We'll take 5 minutes to do this now.
I'll walk around and help where I can, but please don't hesitate to ask your neighbors if you're having problems.
TIMEBOXED TO 5 MINUTES.
I am standing right in front of you so you can tell what I look like
But on the left is what my 8 year old daughter Olivia thinks I look like
On the right is what my 10 year old daughter Lila thinks I look like
and if you think this is just an excuse to talk about my kids
and show you their artwork
You are correct.
Senior Engineer
our mission is to expand the art market,
and we're doing that with a platform for collecting and discovering art.
17 exercises
because you're going to need more than react to build an app
I want you to be prepared to build your next app.
linked from README
linked from README
view our kitten friends
change theme
details about a kitten
Now is your chance to break!
You + me
You + your neighbor
Teaching is one of the best ways to learn
(meet neighbors comes later)
Q's:
How did you like the format?
Amount & depth of content?
React devs?
JavaScript/UI devs?
What UI tools are they using? Angular? Ember? jQuery?
I've been working on front-end web apps for 20 years
I've built them with many different kinds of JavaScript tools
Hard to scale the codebase
If you got it to work, you shipped it.
Angular 1, Knockout, Backbone, ...
Angular 1, Knockout, Backbone, ...
sometimes...
other times.
Regardless, an improvement!
My favorite way to build a web app
My favorite way to build a web app
When we talk about tools, it's helpful to know what problem they solve.
That's the problem react solves...
There are a bunch of things that react is...
the most significant one, for me, is...
React makes it easy to scale simple, understandable code to a large app.
simplicity: hope you can experience today
scalability:
number of components in Facebook's codebase
source: https://medium.com/@dan_abramov/hey-thanks-for-feedback-bf9502689ca4
A JavaScript library for building user interfaces
from their site
dumb - of course that's what it is
but there's meaning here - it is ONLY a UI framework
And nothing more
(maybe the C)
Choosing between Angular and React is like choosing between buying an off-the-shelf computer and building your own with off-the-shelf parts.
What does that mean?
Well, it means we write code that says WHAT to display
not HOW to display it
imperative
declarative - abstracts the imperative
Obviously some code, at some point,
has to be written on HOW to display
But we abstract the imperative code
So our app can call it declaratively.
var kittensComingToMyParty = [];for (var i = 0; i < kittens.length; i++) { var kitten = kittens[i]; if (kitten.rsvp === true) { kittensComingToMyParty.push(kitten); }}
if I wanted to know which kittens were coming to my party, I could write code like this
imperative
var kittensComingToMyParty = [];for (var i = 0; i < kittens.length; i++) { var kitten = kittens[i]; if (kitten.rsvp === true) { kittensComingToMyParty.push(kitten); }}
var kittensComingToMyParty = kittens.filter( kitten => kitten.rsvp === true);
if I wanted to know which kittens were coming to my party, I could write code like this
imperative
...
declarative
...
Why do we care about this?
Declarative views make your code more predictable and easier to debug.
When your code is less worried about HOW to do things,
it's less cognitive load on the reader.
Build encapsulated components that manage their own state, then compose them to make complex UIs.
(read)
...
We'll talk more about components in a bit
...
but before we do, we need to take a step backwards from even React
Let's talk about JavaScript.
There are some JS tools we're going to be using today
that you might have heard of
problem solved: makes it easier to manage dependencies
looking at the history of building web apps
oldest way (1): download a library & put it in a folder
old way (2): bower
new way (3): npm
...
we're going to use npm for a couple things
to use npm, you need a manifest to identify all your dependencies.
this is the package.json.
A cool thing the package.json also allows you to do is define scripts
so 2. we'll run app & tests, via npm scripts
rolling 28 day downloads - over 25 million
NPM usage is growing at a crazy rate
Tons of packages
That means the user might have to download a ton of different libraries to their browser to run our app.
so...
problem solved: large javascript downloads
removes unused code
compresses & minifies our bundle
good development experience
problem solved: JavaScript for the web didn't change at all between 1999 and 2009.
It changed very little from 1999 to 2015.
But we've learned a lot about programming languages since 1999.
Especially the importance of readability & developer experience.
We're now getting new versions of JavaScript via the EcmaScript specification
And it's bringing lots of developer-friendly changes.
...
Let's look at some of those changes, because there are some that impact the way we can write React code.
git checkout master
git pull
npm run test-exercise-1
Components are React's biggest contribution to web dev
It's a mindset shift
When we were doing MVC/MVVM,
we used to think in terms of "controllers" and "views"
We did this because of the principle of "separation of concerns"
But React says controllers & views are the same concern - a component.
so instead of this page having a controller & a view,
it really has a handful of components
building blocks
and each of those components is a concern
and it goes further than that -
each of those components is composed...
of more components
The deconstruction of UI's into small components is crucial to successful React development.
It's easy to think we are doing this to make reuse easier
But it's really more about isolation
Components are like building blocks.
Smaller ones that do fewer things are easier to understand
And easier to assemble (so they aid reuse).
import React from 'react';function FriendsList { // ...Details about this component}
[a component function...] should be pure, meaning that it does not modify component state, it returns the same result each time it’s invoked, and it does not directly interact with the browser.
Ideally, these component functions should be pure
...
2 parts to "pure":
1.doesn't modify state
2.returns same value with same args
...
let's look at some examples of pure & impure functions
to give you clarity on the difference
function add(a, b) { return a + b;}
function add(a, b) { return a + b;}
our example from earlier
pure
doesn't modify the state of anything
returns the same result each time
function now() { return new Date();}
function now() { return new Date();}
impure
it doesn't return the same result each time
function Friend() { this.x = this.x - 1;}
function Friend() { this.x = this.x - 1;}
impure
it modifies the state of the component
note: I used the worried face, not sad face
cuz impure functions cause you to worry about side-effects & unpredictability
Conceptually, components are like JavaScript functions. They accept arbitrary inputs (called “props”) and return React elements describing what should appear on the screen.
component = function of props, the result of which is output to browser
function Friend(props) { console.log(props.name, props.url);}
How do we actually get something on the screen?
we know it goes in our function
but what actually renders markup?
function Friend() { return React.createElement('div');}
create a simple element
function Friend() { return React.createElement('div');}
<div />
create a simple element
emits a div
function Friend() { return React.createElement('button');}
<button />
other element types
function Friend() { return React.createElement('div', { id: 'friend-wrapper' });}
<div id="friend-wrapper" />
2nd argument (optional): "props"
props to apply to rendered element
pass as many props as you want
function Friend() { return React.createElement('div', { id: 'friend-wrapper' }, 'Hi!');}
<div id="friend-wrapper">Hi!</div>
3rd arg (optional): children
text node, in this example
function Friend(props) { return React.createElement( 'div', { id: 'friend-wrapper'}, props.name );}
<div id="friend-wrapper">Potatoes</div>
props as text node!
Outputs are re-rendered as their inputs change
React handles this for you!
function Friend(props) { if (props.name === 'Potatoes') { return React.createElement('div', null, 'We`re best friends!'); } return React.createElement('div', null, 'We`re not best friends.');}
since our component is pure javascript
we can do things like conditional rendering
function Friend(props) { if (props.name === 'Potatoes') { return React.createElement('div', null, 'We`re best friends!'); } return React.createElement('div', null, 'We`re not best friends.');}
<div>We`re best friends!</div>
since our component is pure javascript
we can do things like conditional rendering
function Friend(props) { if (props.name === 'Potatoes') { return React.createElement('div', null, 'We`re best friends!'); } return React.createElement('div', null, 'We`re not best friends.');}
<div>We`re best friends!</div>
<div>We`re not best friends.</div>
since our component is pure javascript
we can do things like conditional rendering
function Friend(props) { return React.createElement( 'div', null, React.createElement('h1', null, 'Hello, ' + props.name) );}
<div> <h1>Hello, Potatoes</h1></div>
nest calls to React.createElement
emits nested elements
...
you can build an entire React app using React.createElement.
but here's the problem with react.createlement.
Most of the things we render to a webpage aren't 1 or 2 html elements.
<div id="friend"> <div id="title"> <h1>Potatoes</h1> <h2>4 months</h2> </div> <div id="photo"> <a href="/friends/potatoes"> <img src="/friends/potatoes/image" alt="Potatoes" /> </a> </div></div>
They're often more like this - and this is even on the small side.
(describe html)
...
here's what our createElement statement looks like for this -
function Friend(props) { return React.createElement( 'div', { id: 'friend' }, React.createElement( 'div', { id: 'title' }, React.createElement('h1', null, props.name), React.createElement('h2', null, props.age) ), React.createElement( 'div', { id: 'photo' }, React.createElement( 'a', { href: '/friends/' + props.id, }, React.createElement('img', { src: props.profileImageUrl, alt: props.name, }) ) ) );}
This is not that easy to read
I'd even describe it as "barfy"
...
this is where another library comes in
JSX is an XML-like syntax extension to ECMAScript without any defined semantics.
It's XML in Javascript
...
here's what it looks like
function Friend() { return React.createElement('div');}
as createElement: An example from earlier.
function Friend() { return React.createElement('div');}
function Friend() { return <div />;}
as createElement: An example from earlier.
as jsx: Looks like the markup we want to render!
function Friend(props) { return React.createElement( 'div', null, React.createElement('h1', null, 'Hello, ' + props.name) );}
another example - more complicated
function Friend(props) { return React.createElement( 'div', null, React.createElement('h1', null, 'Hello, ' + props.name) );}
function Friend(props) { return ( <div> <h1>Hello, {props.name}</h1> </div> );}
another example - more complicated
{} - you want to evaluate a js expression there
...
why would we do this to ourselves?
why put xml in javascript?
it's like a horror film from 2004.
function Friend(props) { return React.createElement( 'div', { id: 'friend' }, React.createElement( 'div', { id: 'title' }, React.createElement('h1', null, props.name), React.createElement('h2', null, props.age) ), React.createElement( 'div', { id: 'photo' }, React.createElement( 'a', { href: '/friends/' + props.id, }, React.createElement('img', { src: props.profileImageUrl, alt: props.name, }) ) ) );}
remember our most complicated createElement example ?
function Friend(props) { return ( <div id="friend"> <div id="title"> <h1>{props.name}</h1> <h2>{props.age}</h2> </div> <div id="photo"> <a href={'/friends/' + props.id}> <img src={props.profileImageUrl} alt={props.name} /> </a> </div> </div> );}
you can build an entire app with createElement
we'll use it today because it makes your code much more readable
...
who still doesn't like this idea?
(poll for separation of concerns)
REPEAT THEIR ANSWER!!!!
React components with JSX look like HTML + JS
the instinct is to separate them
Instead of artificially separating technologies by putting markup and logic in separate files, React separates concerns with loosely coupled units called “components” that contain both.
A component's concern is rendering
It's that friend
or that list of friends
And it takes HTML + JS to render them
You can think of components changing the way we're slicing the app
...
The rub...
So we have to transpile.
It is an extra step between our code & the browser.
But remember, we're already using webpack to minimize our dependencies from npm
function Friend(props) { return ( <div id="friend"> <div id="title"> <h1>{props.name}</h1> <h2>{props.age}</h2> </div> <div id="photo"> <a href={'/friends/' + props.id}> <img src={props.profileImageUrl} alt={props.name} /> </a> </div> </div> );}
Does anyone want to guess what this transpiles to?
function Friend(props) { return React.createElement( 'div', { id: 'friend' }, React.createElement( 'div', { id: 'title' }, React.createElement('h1', null, props.name), React.createElement('h2', null, props.age) ), React.createElement( 'div', { id: 'photo' }, React.createElement( 'a', { href: '/friends/' + props.id, }, React.createElement('img', { src: props.profileImageUrl, alt: props.name, }) ) ) );}
New features to make it easier to work with objects
different things you can do in jsx
return
5* thingscan return five things*
(actually more but we're only going to talk about 5)
return
return
function Friend(props) { return ( <div> {props.name} </div> );}
return a DOM node
return
function Friend(props) { return ( <FriendImage friend={props.friend} /> );}
return another user-defined component.
return
return
function Friend(props) { return [ <FriendImage friend={props.friendA} />, <FriendImage friend={props.friendB} />, ]}
Let you return multiple elements
it's more valuable than you think
this example is not something you'll do a lot of...
return
function Friend(props) { return props.friends.map(friend => <Friend name={friend.name} /> );}
but this is!
mapping from an array to an array of react components.
return
return
import React, { Fragment } from 'react';function Friend(props) { return ( <Fragment> <FriendImage friend={props.friendA} /> <FriendImage friend={props.friendB} /> </Fragment> );}
Nicer syntax than returning an array,
if you have multiple items to return
Fragment does not render anything to the DOM
It used to be that you had to wrap things in a div, instead of a fragment (pollution)
return
import React from 'react';function Friend(props) { return ( <> <FriendImage friend={props.friendA} /> <FriendImage friend={props.friendB} /> </> );}
Shortcut for rendering a fragment
return
return
function Friend(props) { return props.name;}
These are rendered as text nodes in the DOM.
return
return
function Friend() { return null;}
renders nothing.
you could do this, but you won't.
..
you WILL do this, though
return
function Friend(props) { if (props.isLoading) { return null; } return <div>{props.name}</div>}
early-exit from a component
the different types of things a component can render
The tag < friendsList > is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter.`
Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.`
function Friend(props) { <div>{props.name}</div>;}
you'll write components like this, many times
the problem?
function Friend(props) { return <div>{props.name}</div>;}
it's not actually RETURNING the jsx.
export default function(props) { return <div> {props.name} </div>;}
another thing you'll do plenty of times
what's wrong?
that return is returning!
export default function(props) { return ( <div> {props.name} </div> );}
we need parens on the same line, to tell it not to just terminate that statement.
with modern editors this is harder to do now than it was a year ago, but still something to be aware of.
Our Component Inputs
function Friend(props) { props.name = 'Mr. Cat The Mystery Cat'; return <div>{props.name}</div>}
If a prop needs to be changed...
it's probably state (not a prop)
We've seen in some examples already
function Friend(props) { if (props.isLoading) { return null; } return <div>{props.name}</div>;}
We've seen in some examples already
props
arg is passed in
and it contains a property for each individual prop
function Friend(props) { const { isLoading, name } = props; if (isLoading) { return null; } return <div>{name}</div>}
object destructuring
function Friend({ isLoading, name }) { if (isLoading) { return null; } return <div>{name}</div>}
argument destructuring
specify defaults for props
function FriendProfile({ name, image }) { return <img alt={name} src={image} />}FriendProfile.defaultProps = { image: 'http://placekitten.com/200'}
specify defaults for props
assign to the function afterward
function FriendProfile( { name, image = 'http://placekitten.com/200' }) { return <img alt={name} src={image} />}
with argument destructuring
Every component gets a special prop
function Title({ children }) { return <h1 id="title">{children}</h1>;}
Every component gets a special prop
...
example: a Title component
Title component gets children passed in
function Title({ children }) { return <h1 id="title">{children}</h1>;}
<Title>Hello, friends!</Title>
Every component gets a special prop
...
example: a Title component
Title component gets children passed in
...
the children are everything inside of the Title element
function Title({ children }) { return <h1 id="title">{children}</h1>;}
<Title>Hello, friends!</Title>
<h1 id="title">Hello, friends!</h1>
Every component gets a special prop
...
example: a Title component
Title component gets children passed in
...
the children are everything inside of the Title element
...
this is what gets emitted to the DOM
note the children being wrapped inside the div
...
children prop is really powerful.
allows us to compose our components, instead of inherit them
easier to assemble building blocks
easier to refactor/change
(less brittle than inheritance)
props.children
building components that aid composition
With so many components flying around, nested deeply inside each other...
how do we make sure our component is getting the data it needs?
You don't need them to build an app
npm install --save prop-types
Used to be part of React
Now an external library
import propTypes from 'prop-types';function FriendProfile({ name, image }) { return <img alt={name} src={image} />;}FriendProfile.propTypes = { name: propTypes.string.isRequired, image: propTypes.string.isRequired,}
syntax: append after
Friends.propTypes = { friends: PropTypes.arrayOf( PropTypes.shape({ id: PropTypes.number.isRequired, name: PropTypes.string.isRequired, }) ).isRequired,};
arrays
shapes
isRequired
You fire up a browser, view your component, & see an error in the console
index.js:1446 Warning: Failed prop type: The prop `age` is marked as required in `FriendProfile`, but its value is `undefined`. in FriendProfile (at Exercise.js:10) in Friends (at Exercise.js:5) in Exercise (at App.js:13) in div (at App.js:12) in div (at App.js:7) in App (at exercise-6/index.js:6)
This is what the error looks like
propTypes are a development tool
interface FriendProfileProps { name: string; image: string;}function FriendProfile(props: FriendProfileProps) { const { name, image } = props; return <img alt={name} src={image} />;}
type FriendProfileProps = { name: string; image: string;}function FriendProfile(props: FriendProfileProps) { const { name, image } = props; return <img alt={name} src={image} />;}
looks similar to ts
also compile-time
better tooling from typescript
...
If I were starting a new project, I would use TypeScript.
most important thing to know:
gone are the days of one massive stylesheet for an entire app
with react's focus on components, we want to put our styles with our components.
...
A few different approaches.
.friend { color: blueviolet; border-bottom: 1px solid blueviolet;}
friend.css
import './friend.css';function Friend({ name }) { return <div className="friend">{name}</div>});
friend.jsx
Note that this works because we're using webpack with style-loader
(it effectively turns our css into a js object that we can import)
- modular and reusable CSS!
- No more conflicts.
- Explicit dependencies.
- No global scope.
the problems css-modules aims to solve:
.friend { color: blueviolet; border-bottom: 1px solid blueviolet;}
friend.css
import styles from './friend.css';function Friend({ name }) { return <div className={styles.friend}>{name}</div>}
friend.jsx
what it looks like in your code:
.Friend__friend__31BtE { color: blueviolet; border-bottom: 1px solid blueviolet;}
CSS
<div class="Friend__friend__31BtE">Mr. Turtle</div>
HTML
what gets shipped to the browser
import styled from 'styled-components';const brandColor = 'blueviolet';const StyledFriend = styled.div` color: ${brandColor}; border-bottom: 1px solid ${brandColor};`;function Friend({ name }) { return <StyledFriend>{name}</StyledFriend>;}
.31bte { color: blueviolet; border-bottom: 1px solid blueviolet;}
CSS
<div class="31BtE">Mr. Turtle</div>
HTML
shipped to the browser
soooo many ways
Find the way that works for you, and do it unapologetically
Scott Hanselman
People who use css-in-js REALLY like it
I've never felt like managing CSS scope was a problem
But it might depend on what you're building -
Component library probably warrants css-in-js
the important thing is that you have component-based css
via imported css, imported scss, css modules, css-in-js....
The main takeaway is that styles are not a separate concern from the rest of the component.
...some things about react router:
import { BrowserRouter, Route } from 'react-router-dom';function App() { return ( <BrowserRouter> <div className="App"> <Route exact path="/" component={FriendsList} /> <Route path="/lists/:id" component={ListDetail} /> <Route path="/friends/:id" component={FriendDetail} /> </div> </BrowserRouter> );}
Almost everything in react router is a component
import { BrowserRouter, Route } from 'react-router-dom';function App() { return ( <BrowserRouter> <div className="App"> <Route exact path="/" component={FriendsList} /> <Route path="/lists/:id" component={ListDetail} /> <Route path="/friends/:id" component={FriendDetail} /> </div> </BrowserRouter> );}
Almost everything in react router is a component
in most SPA frameworks, you define routes up front
with React Router, routes are part of the component tree
so the routing happens as things are rendering
import { BrowserRouter, Route } from 'react-router-dom';function App() { return ( <BrowserRouter> <div className="App"> <Route exact path="/" component={FriendsList} /> <Route path="/lists/:id" component={ListDetail} /> <Route path="/friends/:id" component={FriendDetail} /> </div> </BrowserRouter> );}
in most SPA frameworks, you define routes up front
with React Router, routes are part of the component tree
so the routing happens as things are rendering
...
look - the router/route components are in the return statement
It's a subtle difference
But it allows you to write declarative routing
which is pretty easy to follow.
import { BrowserRouter, Route } from 'react-router-dom';function App() { return ( <BrowserRouter> <div className="App"> <Route exact path="/" component={FriendsList} /> <Route path="/lists/:id" component={ListDetail} /> <Route path="/friends/:id" component={FriendDetail} /> </div> </BrowserRouter> );}
key components you'll use:
A Router that targets the browser
uses the HTML5 history API to keep your UI in sync with the URL.
you'll generally put this at the top of your app
import { BrowserRouter, Route } from 'react-router-dom';function App() { return ( <BrowserRouter> <div className="App"> <Route exact path="/" component={FriendsList} /> <Route path="/lists/:id" component={ListDetail} /> <Route path="/friends/:id" component={FriendDetail} /> </div> </BrowserRouter> );}
a few key arguments to a Route component
<Route path="/" component={FriendsList} exact />
<Route path="/" component={FriendsList} exact />
<Route path="/lists/:id" component={ListDetail} />
<Route path="/lists/:id" component={ListDetail} />
which component handles this route?
<Route path="/" component={FriendsList} exact />
does the path need to match exactly?
without this, the root path would always match.
...
That's all it takes to define your routes!
The other components you'll use from React-Router are for navigating
import { Link } from 'react-router-dom';function FriendInList({ friend }) { return <Link to={'/friends/' + friend.id}>{friend.name}</Link>;}
effectively an anchor/href
import { NavLink } from 'react-router-dom';function FriendInList({ friend }) { return ( <header> <NavLink to={'/'}>Home</NavLink> <NavLink to={'/friends'}>Friends</NavLink> <NavLink to={'/lists'}>Lists</NavLink> </header> );}
To get your intuition in line with React Router’s, think about components, not static routes. Think about how to solve the problem with React’s declarative composability because nearly every “React Router question” is probably a “React question”.
can use to:
Who has heard things about managing state in react?
...
state mgmt in react is a controversial topic.
but it has gotten less controversial with more recent releases
this is where we were
a component is a function of the props passed into it
but that's not the only input to a component
a component is a function of props & state
...
distinction:
props are passed into a component
state is contained within a component
Think about it from a responsibility viewpoint
Who should be the source of truth for this data?
This component? It's state.
Someone else? It's props passed in.
To manage state, we'll use a new React feature named hooks
Hooks are functions you can use in a Component to perform impure actions from a pure function.
Remember we said our components are pure functions,
and pure functions shouldn't have side-effects, or change state
Hooks allow us to do that safely,
and in a way that allows React to re-render components when it needs to
useState
useEffect
useReducer
useMemo
Examples
All begin with "use" - allows React to identify hooks that you create
useState
our focus for state mgmt
useState
function FriendCounter() { const state = useState(99); const count = state[0]; const setCount = state[1]; return ( <div> <h1>{count}</h1> </div> );}
...
Why do we get back an array?
So we can do this:
useState
function FriendCounter() { const [count, setCount] = useState(99); return ( <div> <h1>{count}</h1> </div> );}
useState
function FriendCounter() { const [count, setCount] = useState(99); return ( <div> <h1>{count}</h1> <button onClick={() => setCount(count + 1)} /> </div> );}
To use the state modifier,
We might add an onClick handler
build a component that uses state to manage which view is being shown
That means never in a conditional or loop
useState
mattersif I've got components down here that need the same state...
I can elevate it to the nearest common ancestor
and manage the state there
You can call useState
more than once
function FriendForm() { const [firstName, setFirstName] = useState(''); const [lastName, setLastName] = useState(''); // ...}
Example: two state props, one for each name
function FriendForm() { const [firstName, setFirstName] = useState(''); const [middleInitial, setMiddleInitial] = useState(''); const [lastName, setLastName] = useState(''); const [nickname, setNickname] = useState(''); const [age, setAge] = useState(0); // ...}
But you can see how this can get out of hand, if you have a lot of form data
Here's 5 fields, maybe you even have more
...
There are a few ways to address this
function useFriendForm() { const [firstName, setFirstName] = useState(''); // ... return { firstName, middleInitial, ... }}function FriendForm() { const form = useFriendForm(); // ...}
useReducer
const initialState = { count: 0 };function reducer(state, action) { switch (action.type) { case 'increment': return { count: state.count + 1 }; case 'decrement': return { count: state.count - 1 }; default: throw new Error(); }}function FriendCounter({ initialState }) { const [state, dispatch] = useReducer(reducer, initialState); return ( <> Count: {state.count} <button onClick={() => dispatch({ type: 'increment' })}>+</button> <button onClick={() => dispatch({ type: 'decrement' })}>-</button> </> );}
Or sometimes, using an external library might be an answer.
The one we use for managing form data is called Formik.
Side effect = interacting with anything outside of React & your React components.
useEffect
useEffect
function Component(props) { useEffect( // arg 1: a function () => { A; // what to do when effect fires return () => { B; // what to do to clean up effect } }, // arg 2: an array [ C ] // variables which should prompt effect to re-fire when changed ); return ...}
2 args, but 3 important things specified
Examples:
useEffect
import callApi from './callApi';function DetailItem(props) { const [item, setItem] = useState({}); useEffect(() => { async function load() { const item = await callApi(props.id); setItem(item); } load(); }, []); return ( ... )}
Loading data from an external source
useEffect
function FriendsChart(props) { useEffect(() => { const graph = graphingLibrary.init(props.data); return () => { graph.destroy(); }; }, []); return ( ... )}
Integrating with a charting library
useEffect
function Chat(props) { useEffect(() => { socket.emit('join', { id: props.friendId }); return () => { socket.emit('leave', { id: props.friendId }); } }, [ props.friendId ]) return ( ... )}
Integrating with websockets
from an api
& rendering components based on the results
function Component() { useEffect(() => { // Load Data }); useEffect(() => { // Charting }); useEffect(() => { // Websockets });}
If you have multiple things happening in an effect,
break them up into separate calls
No reason you can't do that
Each one will be run.
Advantage: easier to follow code
app state is state that the entire app needs, or distant portions of it need.
...
if we're elevating state, this means we have to pass props basically through the entire app
this is called...
all this prop drilling can feel like a burden, and noisy
and for app-level state, there's a fairly new api in react
new in React 16.3
how context solves prop drilling
the top-most component will use the context as a "provider"
bottom components as "consumers"
anything under provider will have access to data from this context
w/o having to have props passed in
so props don't have to be drilled
const UserContext = React.createContext();
the first thing you have to do is create a context
this doesn't happen inside of a component
it's something that executes alongside your components
you get a provider & a consumer
import UserContext from './user-context.js';function UserProvider({ children }) { const user = useUser(); // however you get the logged in user return <UserContext.Provider value={user}>{children}</UserContext.Provider>;}
-
the provider will get wrapped around your component tree
import UserContext from "./user-context.js"function UserProvider({children}) { const user = ... // however you get the logged in user const handleUserChanged = ... // however you change the logged in user return ( <UserContext.Provider value={{ user: user, onUserChanged: handleUserChanged, }} > {children} </UserContext.Provider> );}
value can be an object, if there are multiple things you want to pass down
including actions that will change the value
import UserProvider from './user-provider.jsx';function App() { return ( <UserProvider> <MyComponentTree /> </UserProvider> );}
Then this wrapped provider gets imported into App & rendered at the top of the tree
import React, { useContext } from 'react';function UserName() { const value = useContext(UserContext); return <div>{value.user.name}</div>;}
and then all the places in the subtree that you need access to the state, you have consumers
and they just want to know what the value of the context is
using a hook named useContext
function CurrentUser() { const value = useContext(UserContext); return ( <div> Current User: {value.userName} <button onClick={value.onUserChanged}>Change User</button> </div> );}
for multiple things passed down via context...
Poll: on your own?
Or have me show you the code?
Redux is a predictable state container for JavaScript apps.
one store to hold all the data
and you connect your components to that state,
so that when it changes, your component re-renders.
Simple, scalable state management
reactive programming, observables
updates get automatically applied
Component state: use useState
Distant components: use context or Redux/mobx
we've looked at only three hooks; docs have:
react-use
Prior to v16.8
Introduced recently
Used by legacy react components
legacy components are classes that extend React.Component.
import React from 'react';class FriendDetail extends React.Component { // ...Details about this component}
The only requirement for a react class component
is that it implements a render() function
class FriendDetail extends React.Component { render() { return <div>...</div>; }}
Note: class instance methods!
Props are accessed differently
class FriendDetail extends React.Component { render() { return ( <div> <h1>{this.props.name}</h1> </div> ); }}
props are accessed on "this"
10 minutes
convert between a modern & legacy component
10 minutes
Complete and ready to set-up JavaScript testing solution. Works out of the box for any React project.
import aFunctionUnderTest from './a-function-under-test.js';describe('A set of tests', () => { it('does a certain thing', () => { const result = aFunctionUnderTest(); expect(result).toEqual('a value'); });});
describe
it
expect
expect(a).toEqual(b);expect(a).not.toEqual(b);expect(a).toBeGreaterThan(b);expect(a).toBeNull();expect(a).toBeUndefined();
35 available, some are valid only against certain types
Simple and complete React DOM testing utilities that encourage good testing practices.
...
there are 3 things you're going to do in a test using react-testing-library:
import { render } from 'react-testing-library';import FriendDetail from './FriendDetail';describe('FriendDetail', () => { it('renders something', () => { const friend = { ... }; const context = render( <FriendDetail friend={friend} /> ); // ... });});
you're going to render your component
find elements that were rendered
...
and find them the way a user would -
const context = render(<FriendDetail ... />); const loadingText = context.queryByText('Loading...');
find elements that were rendered
...
and find them the way a user would -
const context = render(<FriendDetail ... />); const loadingText = context.queryByText('Loading...');
const context = render(<FriendDetail ... />); const friendImage = context.queryByAlt('Mr. Cat The Mystery Cat');
find elements that were rendered
...
and find them the way a user would -
const context = render(<FriendDetail ... />); const loadingText = context.queryByText('Loading...');
const context = render(<FriendDetail ... />); const friendImage = context.queryByAlt('Mr. Cat The Mystery Cat');
const context = render(<FriendDetail ... />); const loadingText = context.queryByTestId('friend-container');
find elements that were rendered
...
and find them the way a user would -
you might make assertions against what was rendered & found
import { render, fireEvent } from 'react-testing-library';describe('...', () => { it('...', () => { const context = render(<FriendDetail ... />); const button = context.queryByText('Details >'); fireEvent.click(button); });});
To test interactions of components,
you'll fire DOM events against the elements,
using fireEvent.
enzyme does a LOT of stuff;
react-testing-library tries to keep the possibilities to things that users care about
for more matchers that let you identify classes, attributes, things on DOM elements
it('shows details after clicking button', async () => { const context = render(<FriendDetail />); const button = context.getByText('Details'); fireEvent.click(button); const details = await waitForElement(() => context.getByText('snuggles like a champion')); // ... });
specific to react-testing-library
async events are usually hard to test
but not with rtl
async wait async waitForElement async waitForDomChange
Async helpers available
more general suggestions:
testing logic outside of components is easier than testing logic inside of components
Avoid testing for presence of css classes, unless it's the only way to prove something is working
Prefer testing presence of text elements
react-testing-library forces you to do this
unit tests are small
& people can be overly dogmatic about what defines a "unit"
integration tests are too slow
& we can tell a lot from our app without pulling in db/api's
somewhere in the middle of those is the "unigration" zone
if you're trying to test that component at the top of this subtree,
test component tree with all helper functions included
mock things that are slow/we don't own (api's)
You have all the context right now. You three months from now does not. Consider what they'll go through when they find a failing test! Make it easier for them to figure out.
problem solved: setting up webpack & react & other dependencies is difficult
spins up an app for us
sensible defaults
hides complexity of webpack, etc from us
if I were starting an app, I would use create-react-app.
Try to make as many simple, dumb, functional components as possible
Push the complicated things & stateful things to the edges
This makes it easier to understand most of your app
In general, this is a good strategy for problem-solving
It also is in regards to React
But it is ALSO a good strategy to make sure your components are small & easy to maintain.
I mean this in a couple ways:
markup + js + css
but also,
When grouped by file type, you have to bounce around to work on a feature
When grouped by feature, all you need is in one folder
I want to thank you for your time
I really appreciate it.
Survey
Questions after
Thank you!
Keyboard shortcuts
↑, ←, Pg Up, k | Go to previous slide |
↓, →, Pg Dn, Space, j | Go to next slide |
Home | Go to first slide |
End | Go to last slide |
Number + Return | Go to specific slide |
b / m / f | Toggle blackout / mirrored / fullscreen mode |
c | Clone slideshow |
p | Toggle presenter mode |
t | Restart the presentation timer |
?, h | Toggle this help |
Esc | Back to slideshow |