Skip to content

Instantly share code, notes, and snippets.

@deependhamecha
Last active June 10, 2023 07:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save deependhamecha/c8d4c7f5b3c1da14c6ae3eafa767d3b7 to your computer and use it in GitHub Desktop.
Save deependhamecha/c8d4c7f5b3c1da14c6ae3eafa767d3b7 to your computer and use it in GitHub Desktop.
React 2023

React

Create App

npm install create-react-app
create-react-app my-app

Simple Component

App.js

function App() {
  return (
    <div className="App">
      <h1>Lets get Started</h1>
      <ExpenseItem />
      <ExpenseItem />
      <ExpenseItem />
    </div>
  );
}

export default App;

index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <App />
);

Props

<ExpenseItem title={title} date={date} />
function ExpenseItem(props) {
  const title = props.title;
  const date = props.date;
}

ng-content in React (known as Composition)

<Card />
<ExpenseItem />
<div></div>
</Card>
<div>
  {props.children}
</div>

ngClass in React

<Card className="expense-item">
</Card>
function Card(props) {


    // This line right here
    const classes = 'card '+ props.className;

    return (
    <div className={classes}>
        {props.children}
    </div>
    )
}

export default Card;
<div className={'form-control valid'}></div>
<div className={`form-control ${valid ? 'green': 'red'}`}></div>
<div className={`form-control ${validClass}`}></div>

Remember: Custom Components are not seen in DOM

ngStyle

<p style={{height: variableName, color: 'red'}}>Some Data</p>
<p style={{color: !isValid ? 'green': 'red'}}>Some Data</p>

Events

<button onClick={() => {console.log('Clicked!');}}>
const clickHandler = () => {
    console.log('Clicked!');
}
<button onClick={clickHandler}>Change Title</button>

Why State?

function ExpenseItem(props) {
  let title = props.title;

  const clickHandler = () => {
    title = 'Deepen'
  }
}
<div onClick={clickHandler}></div>

This wont re-render the JSX

const [title, setTitle] = useState(props.title);

Must be called inside Component functionand not inside any Handler or outside component function.

Use State with Objects

const [obj, setObj] = useState({a: 0, b: 0});

const onChange = (event) => {
  // setObj({...obj, a: event.target.value}); // Dont do this.

  setObj((prevState) => {
    return {...prevState, a: event.target.value};
  })
}

Form

onSubmit does not do e.preventDefault. We need to do it manually.

@Output

Parent Component

const saveDataHandler = (data) => {

};
<Expense saveData={saveDataHandler}>

Child Component

props.saveData('someValue'); // just like .emit()

Key

{data.map((e, i) => <div key={i}></div>)}
{data.map((e, i) => <div key={e.id}></div>)}

If Condition

{someCondition ? <p>Yes</p>: <p>No</p>}

{someCondition && <p>Yes</p>}
let expensesCount = <p>No expenses found.</p>
if(someCondition) {
  expenseCount = filterData.map(e => <p>Some value</p>)
}
{expenseCount}

Shadow DOM

  1. Use a library called styled components
  2. Using CSS Modules
import styles from 'button.module.css'; // Yes, you need to have .module in your filename for it to work.
<button className={styles.button}>
<button className={styles['button']}>
<button className={`${styles['button']} ${!isValid && styles.invalid}`}>
.button {
  color: white;
  backgroundColor: red;
}

Check DOM element.

Check the link for more reference example

Fragments

You need to have one root element for JSX to work. To avoid this use fragments.

<React.Fragment>
  <h2>Hello</h2>
</React.Fragment>
<>
  <h2>Hello</h2>
</>

Portal

Just like ng-container

{ReactDOM.createPortal(
  <SomeComponent
  title={props.title}
  message={props.message}
  onConfirm={props.onConfirm} />,
  document.getElementById('overlay-root')
)}

Ref

Just like Template Reference in Angular

const nameRef = useRef();

// Somewhere
console.log(nameRef.current.value);
<input type="text" ref={nameRef} />

useEffect

import { useEffect, useState } from "react";

export default function Dude() {
    const [count, setCount] = useState(0);

    useEffect(() => {
        console.log('count: ', count);
        if(count !== 0 && count % 3 === 0) {
            console.log('Mod 3!');
        }
    }, [count]); // setCount will not be tracked by effect, only variables(getters)

    const inc = () => {
        setCount((prevState) => prevState + 1);
    }
    

    return (
        <>
        <h1>Count: {count}</h1>
        <button type="button" onClick={inc}>Increment +</button>
        </>
    );
}
  1. You specify variables(getters) in dependencies and it will track those.
  2. If you do not specify any dependencies ([]), then it will only run once and not on every render.
  3. If you do not specify dependency at all, then it will execute on every render and it will same you have specified outside of useEffect.
  4. You DON'T need to add variables or functions you might've defined OUTSIDE of your components (e.g. if you create a new helper function in a separate file): Such functions or variables also are not created inside of a component function and hence changing them won't affect your components (components won't be re-evaluated if such variables or functions change and vice-versa).
useEffect(() => {
  // something
});

There is something called cleanup function

useEffect(() => {
  return () => {
    console.log('Clean up');
  }
}, [someVariable]);

Suppose you want to make a debounce functionality.

useEffect(() => {
  const identifier = setTimeout(() => {
    console.log('Checking form validity!');
  }, 500);
  return () => {
    console.log('Clean up');
    clearTimeout(identifier);
  }
});

useReducer

Context API

There are 3 parts while using Context

  1. Defining the Data(Store)
  2. Provider
  3. Consumer
export default const AuthContext = React.creatContext({
  isLoggedIn: false
});
import AuthContext from '../auth-context';

return (
  <AuthContext.Provider
    value={{
      isLoggedIn: isLoggedIn  // This acts like your selector in your ngrx
        // This is the value which will be passed to your consumer.
    }}
  >  // Consider this in your app.component.html

  </AuthContext.Provider>
)
<AuthContext.Consumer>
  {(ctx) => (
    <button>{ctx.isLoggedIn ? 'Logout': 'Login'}</button>
  )}
</AuthContext.Consumer>

OR

import React, {useContext} from 'react';

const Navigation = (props) => {
  const ctx = useContext(AuthContext);

  return (<button>{ctx.isLoggedIn ? 'Logout': 'Login'}</button>);
}

You can also have/reference functions in context.

Context Limitations:

  1. React Context is NOT optimized for high frequency changes.
  2. React Context also should NOT be used to replace all component communications and props.

React hooks are nothing words starting with use.

Forward Ref and Use Imperative Handle

useCallback

Custom Hooks

Dont know why to make one?

React Router

npm install react-router-dom

Simple Router example,

import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import HomePage from './components/Home';
import ProductsPage from './components/Products';

const router = createBrowserRouter([
  {
    path: '/',
    element: <HomePage/>
  },
  {
    path: '/products',
    element: <ProductsPage />
  }
]);

function App() {
  return <RouterProvider router={router} />;
}

export default App;

Home.js

export default function HomePage() {
    return <h1>Home Page</h1>
}

Products.js

export default function ProductsPage() {
    return <h1>Products Page</h1>
}

OLD APPROACH

const routeDefinitions = createRoutesFromElements(
  <Route>
    <Route paths="/" element={<HomePage />}>
    <Route paths="/products" element={<ProductsPage />}>
  </Route>
);

const router = createBrowserRouter(routeDefinitions);
export default function App() {
  return <RouterProvider router={router} />;
}

Link Navigation

import { Link } from 'react-router-dom';

export default function HomePage() {
    return (<>
        <h1>Home Page</h1>
        <p>
            Go to <Link to="/products">The list of products</Link>.
        </p>
    </>);
}

You can also <a href="/products"> but it will refresh the page.

Nested Routes

const router = createBrowserRouter([
  {
    path: '/',
    element: <RootLayout />,
    children: [
      {
        path: '/',
        element: <HomePage/>
      },
      {
        path: '/products',
        element: <ProductsPage />
      }
    ]
  }
]);

For Error Page.

const router = createBrowserRouter([
  {
    path: '/',
    element: <RootLayout />,
    children: [
      {
        path: '/',
        element: <HomePage/>
      },
      {
        path: '/products',
        element: <ProductsPage />
      }
    ],
    errorElement: <Error />
  }
]);

Active Class to NavLink

<NavLink to="/" className={({isActive}) => isActive ? 'active': undefined}>Home</NavLink>

end will match at the end of to="/". Why so? because by default it will make it active even if its children route. To avoid this, you can use end property.

<NavLink to="/" className={({isActive}) => isActive ? 'active': undefined} end>Home</NavLink>

Programmatic Navigation

export default function HomePage() {
  const navigate = useNavigate();

  function navigateHandler() {
    navigate('/products');
  }

  return (
    <>
      <button onClick={navigateHandler}></button>
    </>
  );
}

Dynamic Routes

const router = createBrowserRouter([
  {
    path: '/products/:productId',
    element: <ProductDetailPage />
  }
]);
import { useParams } from 'react-router-dom';

export default function ProductDetailPage() {
  const params = useParams();

  const prodId = params.productId && params.productId.toUpperCase();

  return (
    <>
    <h1>Product Details!</h1>
    <p>{prodId}</p>
    </>
  );
}

Relative Paths

Above are absolute paths starting with '/'. If you remove starting '/', it will be relative path. Both in route config and to property in <Link>.

You can add relative to <Link> for different parent path.

Go one path back:

<Link to="..">Back</Link>
relative="path"
relative="route"

If you put path, then it will remove from end of the path.

Make Index Route(Page)

{
  index: true, // default route for '/path'
  element: <HomePage />
}

loader (Resolvers in React)

{
  index: true,
  element: <HomePage />,
  loader: async () => {
    const response = await fetch('http://localhost:8080/events');

    if(!response.ok) {
      //..
    } else {
      const resData = await response.json();
      return resData.events; // which contains array of data
    }
  }
}

How to use it?

import { useLoaderData } from 'react-router-dom';

export function HomePage() {
  const data = useLoaderData();
}

This will not load Component until data is resolved.

Show loading state.

export function HomePage() {
  const navigation = useNavigation();

  return (
    <>
    {navigation.state == 'loading' && <p>...Loading</p>}
    </>
  );
}

You can access params in loader

export async function loader({events, params}) {
  const id = params.eventId;
}

useRouteError

Centralized page for errors.

Consider, you there is an error in loader where api fails, so if you throw response then you can get data from error object.

export async function loader() {
  const respnonse = await fetch('http://someurl');
  if(!response.ok) {
    throw new Response(JSON.stringify({message: 'Could not fetch events.'}), {
      status: 500
    }); // Creating Custom Response from frontend
  } else {
    return reponse;
  }
}

You can also use json() from 'react-router-dom' which creates a json response.

const error = useRouteError();

useRouteLoaderData

If you have loader in your parent and want to refer to that loader then,

{
  path: 'event',
  id: 'event-detail',
  children: [
    {
      path: ':eventId',
      ...
    }
  ]
}
import { useRouteLoaderData } from 'react-router-dom';

export function EditEventPage() {
  const data = useRouteLoaderData('event-detail');
}

Form from react-router-dom

?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment