- Complete Intro to React
React
: all about making and resuingComponents
to build large appscreateElement
: create aninstance
of a React Component
- react(~12KB): the api to create React Components.
const App = () => {
return React.createElement(
'div', // html type or custom type -> web components
{}, // props for it and its children: {} [] or null is all fine
'Adopt Me!' // children or content
);
};
- react-dom(~116KB): the api to render React Components to HTML page, similar things are React Native, React 360(VR)...
ReactDOM.render(
React.createElement(App), // root component instance
document.getElementById('root') // entry point in html
);
- Make code more readable
- translate
HTML
tags intoReact.createElement
calls
- You still need to import
React
to useReact.createElement()
behind the hood. - Before
React.createElement("h1", {id:"main-title"}, props.name)
- After
<h1 id="main-title">{props.name}</h1>
- initialize hooks only on the top, don't use them in loops, conditions or nested functions
const App = () => {
const [number, setNumber] = useState(0)
...
}
- use hooks in React Functions: React Component or Other Hooks
- Further Decoupled Code between
parent
andchildren
components
const useDropdown = (label, defaultState, options) => {
const [state, setState] = useState(defaultState); // state manager
const Dropdown = () => (
//...
); // component
return [state, Dropdown, setState] // expose component with state manager
};
- Production mode has much
smaller
size of the bundled javascript - if you use
Parcel.js
, you can do this to change mode
NODE_ENV=development
- give your additional warnings about things that you shouldn't do.
const App = () => {
return <React.StrictMode>
// ...
</React.StrictMode>
}
- Good for handling accessibility
this.func.bind
used to be an expensive operation in browser- Although chrome fix the performance issue of
bind
, but you still need to try to avoid usingbind
since application may run in old browser
- shallow merge
this.state = {a: 1, b:2}
this.setState({
a:2, // override 1
b:3, // override 2
c:4 // add new
})
- Happens before
render()
- usually used to compare
previous state
andcurrent props from parents
to determine data forrendering
- not recommended to use by React
- Fully Controlled Component
function EmailInput(props) {
return <input onChange={props.onChange} value={props.email} />;
}
- Best way: Fully uncontrolled component with a
key
When a key changes, React will create a
new component instance
rather than update the existing one.
class EmailInput extends Component {
state = { email: this.props.defaultEmail };
handleChange = event => {
this.setState({ email: event.target.value });
};
render() {
return <input onChange={this.handleChange} value={this.state.email} />;
}
}
<EmailInput
defaultEmail={this.props.user.email}
key={this.props.user.id}
/>
- EB is just like
Exception Handling
to catch some errors to avoid crashing your applications.
An
Error Boundary Component
must be a Class Component
- EB can only catch erros of its children components
- EB cannot be used in event handlers
- You can use
try/catch
in event handlers
- Simply use
either
methods within a class component, then the component becomes anError Boundary
static getDerivedStateFromError()
: control to display fallback UIcomponentDidCatch()
: side effect like error logging service
happens during render(), thus not allowed for handling any side effects
- used for hadning side effects like
error logging service
// ErrorBoundary.js
class ErrorBoundary extends Component {
constructor(props) {
super(props);
// step 1: intilaize error state
this.state = { hasError: false };
}
static getDerivedStateFromError() {
// step 2: change error state when error happens
return { hasError: true };
}
// step 3: side effect like error logging service
componentDidCatch(error, info) {
console.error("ErrorBoundary caught an error", error, info);
}
// step 4: fallback UI
render() {
if (this.state.hasError) {
return (
<h1>
There was an error with this listing. <Link to="/">Click here</Link>{" "}
to back to the home page or wait five seconds.
</h1>
);
}
return this.props.children;
}
}
export default ErrorBoundary;
componentDidMount() {
throw new Error("lol");
...
}
- Application-Level State, avoid using context until you have to use it.
- Use
Redux
orContext
, don't use them both. - Context is useful to pass
broadcasting
user-data
- usually used to do modal
- This where the modal will actually be mounted whenever we render to this portal. Totally separate from our app root.
<body>
<div id="root">Not Rendered!</div>
<!-- Second Mount Point -->
<div id="modal"></div>
</body>
import React, { useEffect, useRef } from "react";
import { createPortal } from "react-dom";
const modalRoot = document.getElementById("modal");
const Modal = ({ children }) => {
const elRef = useRef(null);
if (!elRef.current) {
elRef.current = document.createElement("div");
}
useEffect(() => {
modalRoot.appendChild(elRef.current);
return () => modalRoot.removeChild(elRef.current);
}, []);
return createPortal(<div>{children}</div>, elRef.current);
};
export default Modal;
toggleModal() {
this.setState({ showModal: !this.state.showModal });
}
adopt() {
navigate(this.state.url);
}
{showModal ? (
<Modal>
<div>
<h1>Would you like to adopt {name}?</h1>
<div className="buttons">
<button onClick={this.adopt}>Yes</button>
<button onClick={this.toggleModal}>No</button>
</div>
</div>
</Modal>
) : null}
- One small and fast solution for
styled-component
import { css } from '@emotion/core';
<header
css={css`
background-color: #333;
position: sticky;
top: 0;
z-index: 10;
`}
>
</header>
- You only need to use
Suspense
once on top of the App
const Details = lazy(() => import('./Details'));
const SearchParams = lazy(() => import('./SearchParams'));
<Suspense fallback={<h1>loading routes ...</h1>}>
<Router>
<SearchParams path="/"></SearchParams>
<Details path="details/:id"></Details>
</Router>
</Suspense>
// Details
const Modal = lazy(() => import("./Modal"))
- Don't need
suspense
anymore - You can easiy specify corresponding
fallback
for each Loadable component - More
readable
const Details = Loadable({
loader: () => import('./Details'),
loading: () => <div>Loading</div>
})
const SearchParams = Loadable({
loader: () => import('./SearchParams'),
loading: () => <div>Loading</div>
})
const Modal = Loadable({
loader: () => import("./Modal"),
loading: () => <div>Loading</div>
})
- Without
SSR
, user has to download html(nothing, just mount point),js, wait for js to run and then see the page. Slow! - With
SSR
, use just download the html and page shows immediately. Fast! SSR
is typically used torender
the page for the first time.SSR
is also beneficial toSearch Engine Optimization
(SEO)
- React
pre-render
the page in the backend - Backend server sends only
markup
for user first time access - Frontend: React is still rendering, once it finshed, it will replace the first time page.
Jest
can be used both for React and Node.js@testing-library/react
is a new recommended and easy way to test React (replacing Enzyme)__tests__
naming are borrowed from python means something magic. (jest will automatically find__tests__
and run it.)- You can name
SearchParams.js
rather thatnSearchParams.test.js
, magic folder names help jest find it. - Without magic folder name, you need assign
SearchParams.test.js
to let testing libs find testing scripts.
- You can name
- If one test including
3rd party API
anddon't want to wait for API response
, we can use mock to replace it.