You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
function handleChange(e: React.ChangeEvent<HTMLInputElement>)
State vs props
props (short for “properties”) and state are both plain JavaScript objects
both hold information that influences the output of render, they are different in one important way:
props = passed to the component and can't be directly changed by this component
state = managed within (can be change by) the component
'children' prop
// Header.jsfunctionHeader({ children }){// props.childrenreturn(<header>{children}</header>);}// App.jsfunctionApp(){consttitle="My title!";// Title will be passed to Header's `props.children`return(<Header><Titletitle={title}/></Header>);}
both check types, Typescript at compile time and PropTypes at runtime
Typescript is enough
importPropTypes,{InferProps}from'prop-types';functionHeader({ title }: InferProps<typeofHeader.propTypes>){return(<><h1>{title}</h1></>);}Header.propTypes={title: PropTypes.string.isRequired,};
React Fragment
// a component can only return one single element// fragments let you group a list of children without adding extra nodes to the DOM<React.Fragment><td>Hello</td><td>World</td></React.Fragment>// shorter<><td>Hello</td><td>World</td></>
this on Class Components
use bind
2 methods:
bind inline (creates a function at every render, therefore not recommended) = onChange={this.handleChange.bind(this)}
bind function inside constructor = this.handleChange = this.handleChange.bind(this);
don't use bind
to avoid use bind, use instead:
declare function with public class field (experimental feature transpiled by babel); public myF = () => {
call function with an arrow functions
however, with this syntax a different callback is created at every render which may cause slowdown specially if callback is being passed as a prop (read more)
State
Functional - useState
Prior to React 16.8, functional components were stateless, now they can hold state via hooks
import{useState}from"react";functionApp(){const[title,setTitle]=useState("My title");// setTitle can also accept a function// this is useful to catch previous state valueconstchangeTitle=()=>setTitle("Title changed");return(<><Headertitle={title}changeTitle={changeTitle}/></>);}
useRef returns a mutable object with only 1 property (current)
functionApp(){// if you're sure that divRef.current will never be null, you can use non-null assertion operatorconstinputRef=React.useRef<HTMLInputElement>(null!);functionhandleClick(){inputRef.current.focus();// if you don't use non-null assertion at ref definition, you've to do type assertion here// (inputRef.current as HTMLInputElement).focus();}return(<><inputref={inputRef}type="text"/><buttononClick={handleClick}>Focus</button></>);}
change ref doesn't trigger re-render
that's the biggest difference between state and ref
another use of refs, is to preserve a value across re-renders
functionApp(){const[count,setCount]=React.useState(0);constincrement=()=>setCount(count+1);// how to count how many re-renders// variable won't work because variables get redeclared at every re-render// const renderCount = 0;// React.useEffect(() => console.log(`re-renders: ${renderCount}`));// state won't because each time state changes a new re-render will occur// which would cause an infinite loop// const [renderCount, setRenderCount] = React.useState(0);// React.useEffect(() => {// setRenderCount(renderCount + 1);// console.log(`re-render: ${renderCount}`);// });// solution: use refconstrenderCount=React.useRef(0);React.useEffect(()=>{console.log(`re-render: ${(renderCount.current+=1)}`);});return(<><h1onClick={increment}>{count}</h1></>);}
useReducer
alternative to useState, uses same pattern used in Redux
is preferable to useState when you have complex state logic
which involves multiple sub-values or when the next state depends on the previous one
constHeader=()=><h1>Header</h1>;// HOC = a function that takes a component and returns a new component// by convention, HOC name usually starts w/ 'with'functionwithRedColor(Comp){returnclassextendsReact.Component{render(){return(<divstyle={{color: 'red'}}><Comp/></div>);}};}// creates a new component from the HOCconstRedHeader=withRedColor(Header);functionApp(){return<RedHeader/>;}
Render props
functionrender(){return<h1>{this.state.value}</h1>;}// a component with a render prop takes a function that returns a React element// and calls it instead of implementing its own render logicclassHeaderextendsReact.Component{constructor(props){super(props);this.state={value: 'from Header'};}render(){returnthis.props.render.call(this);}}constApp=()=><Headerrender={render}/>;
React.PureComponent
in some cases, can boost performance
vs React.Component
similar to React.Component, but implements shouldComponentUpdate which performs a shallow comparison of prop and state
Don't use with nested objects
only use PureComponent when you expect to have props and state with primitive values (not arrays or objects)
complex data structures, may produce false-negatives for deeper differences
Prevents re-rendering
prevents re-rendering if prop or state has been updated with the same values
if they're assigned the same values or if parent component re-render but passed the same props
prevents re-rendering if value didn't changed
classAppextendsReact.PureComponent{constructor(props){super(props);this.state={value: "test",};}componentDidMount(){setInterval(()=>{this.setState({value: "test",// change it to the same value});},1000);}render(){console.log("rendered");// if PureComponent, will log only once because change state to the same value doesn't trigger new renderreturn<h1>Hello, world!</h1>;}}
prevents unnecessary re-rendering
classChildextendsReact.PureComponent{render(){// run only once when component is pure (and twice otherwise)// PureComponent recognizes that the changes in the App's state didn't affect any of the props passed to this componentconsole.log("Child rendered");return(<h1>{this.props.value}</h1>);}}classAppextendsReact.Component{constructor(props){super(props);this.state={val1: "one",val2: "two",};}componentDidMount(){setTimeout(()=>{this.setState({val1: "one...",});},1000);}render(){console.log("App rendered");return(<><h1>{this.state.val1}</h1><Childvalue={this.state.val2}/></>);}}
React.memo
const MyPureComp = React.memo(MyComp);
is a higher order component
it's similar to PureComponents but for function components
performs shallow comparison, accepts a comparison function as optional second argument
Environment variables
create a .env file in the root
add the variables (must start with 'REACT_APP'):
REACT_APP_TEST = "test 1"
reload the server
Context
is useful when sharing global data or data that needs to be accessible by many components at different nesting levels
should be use sparingly because makes reuse more difficult
Steps
React.createContext() and export the Consumer
Wrap component(s) inside Provider
Inside a wrapped component (or one of its child), use the Consumer
Functional components
interfaceMyContextInterface{title: string;}// createContext takes the default context value// this default value will only be used inside Consumers with no matching ProviderconstMyContext=React.createContext<Partial<MyContextInterface>>({});functionHeading(){// // useContext alternative// const context = React.useContext(MyContext)!;// return <h1>{context.title}</h1>;// Context.Consumer alternativereturn(<MyContext.Consumer>{(context)=><h1>{context.title}</h1>}</MyContext.Consumer>);}functionHeader(){constpropsToPass={title: 'My title...',};return(<MyContext.Providervalue={propsToPass}><Heading/></MyContext.Provider>);}
it's a styling convention that let you use the same CSS class name in different files without worrying about naming clashes (everything is locally scoped by default)
when any error happens inside a component, React default behavior it to umount the whole tree and render nothing
Error Boundary
it's a user-defined class component used to handle runtime errors in its children which implements either/both:
static getDerivedStateFromError = to render fallback UI
componentDidCatch = to log error information
classErrorBoundaryextendsReact.Component{constructor(props){super(props);this.state={hasError: false};}staticgetDerivedStateFromError(error){// update state so the next render will show the fallback UIreturn{hasError: true};}componentDidCatch(error,errorInfo){// log errorconsole.warn(error,errorInfo);}render(){if(this.state.hasError){// fallback UIreturn<pstyle={{color: 'red'}}>Something went wrong.</p>;}returnthis.props.children;}}functionTitle(){if(Math.random()<0.5)thrownewError('error inside Title component...');return<h1>Title...</h1>;}functionApp(){return(<ErrorBoundary><Title/></ErrorBoundary>);}
react-error-boundary
npm install --save react-error-boundary
provides:
ErrorBoundary = simple and reusable error boundary
useErrorHandler = hook used to catch errors that cannot be caught with error boundary
import{ErrorBoundary}from'react-error-boundary';functionTitle(){if(Math.random()<0.5)thrownewError('error inside Title component...');return<h1>Title...</h1>;}constTitleFallback=()=><pstyle={{color: 'red'}}>error</p>;functionApp(){return(<ErrorBoundaryFallbackComponent={TitleFallback}><Title/></ErrorBoundary>);}
Some errors aren't caught
error boundary doesn't handles all errors:
it catches errors during rendering, in lifecycle methods/hooks and in constructors
doesn't catch errors in event handlers, async code (e.g. setTimeout), server-side rendering; to those, you can use try/catch
// error won't be caught by error boundaryfunctionTitle(){const[title,setTitle]=React.useState('...');React.useEffect(()=>{(asyncfunctiongetTitle(){constresp=awaitfetch('https://jsonplaceholder.typicode.com/psts/1');if(!resp.ok)thrownewError('fetch failed');// this error won't be caughtconstjson=awaitresp.json();setTitle(json.title);})();},[]);return<h1>{title}</h1>;}// manually using try/catch and a secondary state to caught errors not caught with error boundary// this isn't ideal because duplicates the error handling logicfunctionTitle(){const[title,setTitle]=React.useState('...');const[error,setError]=React.useState(false);React.useEffect(()=>{(asyncfunctiongetTitle(){try{constresp=awaitfetch('https://jsonplaceholder.typicode.com/psts/1');if(!resp.ok)thrownewError('fetch failed');constjson=awaitresp.json();setTitle(json.title);}catch(e){console.warn(e);setError(true);}})();},[]);returnerror ? <pstyle={{color: 'red'}}>error</p> : <h1>{title}</h1>;}// using react-error-boundary's useErrorHandlerimport{ErrorBoundary,useErrorHandler}from'react-error-boundary';functionTitle(){const[title,setTitle]=React.useState('...');consthandleError=useErrorHandler();React.useEffect(()=>{(asyncfunctiongetTitle(){constresp=awaitfetch('https://jsonplaceholder.typicode.com/psts/1');if(!resp.ok)handleError(newError('fetch failed'));constjson=awaitresp.json();setTitle(json.title);})();},[]);return<h1>{title}</h1>;}constTitleFallback=()=><pstyle={{color: 'red'}}>error...</p>;functionApp(){return(<ErrorBoundaryFallbackComponent={TitleFallback}><Title/></ErrorBoundary>);}