React tour - part 1

Posted by : on

Category : react


React immediate tour part 1

Built-in React Hooks

Hooks let you use different React features from your components. You can either use the built-in Hooks or combine them to build your own.

State Hooks

State lets a component remember information like user input.

  • useState: declares a state variable that you can update directly
  • useReducer: declare a state variable with the update logic inside a reducer function
function ImageGallery() {
    const [index, setIndex] = useState(0);
    // ...
}

Extracting State Logic into a Reducer

Component with many state updates spread cross many event handlers can become overwhelming. For these cases, you can consolidate all the state update logic outside your component in a single function, call a reducer.

Example:

import { useReducer } from 'react';
import AddTask from './AddTask.js';
import TaskList from './TaskList.js';

export default function TaskApp() {
  const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);

  function handleAddTask(text) {
    dispatch({
      type: 'added',
      id: nextId++,
      text: text,
    });
  }

  function handleChangeTask(task) {
    dispatch({
      type: 'changed',
      task: task,
    });
  }

  function handleDeleteTask(taskId) {
    dispatch({
      type: 'deleted',
      id: taskId,
    });
  }

  return (
    <>
      <h1>Prague itinerary</h1>
      <AddTask onAddTask={handleAddTask} />
      <TaskList
        tasks={tasks}
        onChangeTask={handleChangeTask}
        onDeleteTask={handleDeleteTask}
      />
    </>
  );
}

function tasksReducer(tasks, action) {
  switch (action.type) {
    case 'added': {
      return [
        ...tasks,
        {
          id: action.id,
          text: action.text,
          done: false,
        },
      ];
    }
    case 'changed': {
      return tasks.map((t) => {
        if (t.id === action.task.id) {
          return action.task;
        } else {
          return t;
        }
      });
    }
    case 'deleted': {
      return tasks.filter((t) => t.id !== action.id);
    }
    default: {
      throw Error('Unknown action: ' + action.type);
    }
  }
}

let nextId = 3;
const initialTasks = [
  {id: 0, text: 'Visit Kafka Museum', done: true},
  {id: 1, text: 'Watch a puppet show', done: false},
  {id: 2, text: 'Lennon Wall pic', done: false},
];

Component logic right now can be easier to read when you separate concerns like this. For more detail, see here

Context Hooks

Context lets a component receive information fro distant parents without passing it as props.

About the tutorial of Context Hooks please refer to React hooks In this topic we just care about Context Hooks performance/

A common way to optimize re-rendering performance is to skip unnecessary work. For example, you can tell React to reuse a cached caclculation or to skip a rerender by using:

  • useMemo: lets you cache the result
  • useCallback: lets you cache a function definition before passing it down to an optimized component
    function TodoList({ todos, tab, theme }) {
    const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);
    // ...
    }
    

Sometimes, you can’t skip re-rendering because the screen actually needs to update. To prioritize rendering, use one of these hooks:

  • useTransition lets you mark a state transition as non-blocking and allow other updates to to interrupt it.
  • useDeferredValue lets you defer updating a non-critical part of the UI and let other parts update first

useTransition:

function TabContainer() {
  const [isPending, startTransition] = useTransition();
  const [tab, setTab] = useState('about');

  function selectTab(nextTab) {
    startTransition(() => {
      setTab(nextTab);
    });
  }
  // ...
}

With a transition your UI stays responsive in the middle of a re-render. If user click to change page, but after that they change their mind and click another tab, they can do without waiting for first rendering finish.

**Building a Suspense-enabled router

function Router() {
  const [page, setPage] = useState('/');
  const [isPending, startTransition] = useTransition();

  function navigate(url) {
    startTransition(() => {
      setPage(url);
    });
  }
  // ...

Another hook is useCallback we need to find out what it does?

import { useCallback } from 'react';

export default function ProductPage({ productId, referrer, theme }) {
  const handleSubmit = useCallback((orderDetails) => {
    post('/product/' + productId + '/buy', {
      referrer,
      orderDetails,
    });
  }, [productId, referrer]);

Parameter

  • fn: the function value that you want to cache
  • dependencies: the list of all reactive values referenced inside of the fn code.

Returns On the initial render, useCallback returns the fn function you have passed. During subsequent renders, it will either return an already stored fn function from the last render (if the dependencies haven’t changed), or return the fn function you have passed during this render.

** Usage **

Skiping re-rendering of components

When you optimize rendering perfromance, you will sometimes need to cache the functions that you pass to child components. Let’s first look at the syntax for how to dothis, and then see in which cases it’s usefull.

function ProductPage({ productId, referrer, theme }) {
  // Every time the theme changes, this will be a different function...
  function handleSubmit(orderDetails) {
    post('/product/' + productId + '/buy', {
      referrer,
      orderDetails,
    });
  }
  
  return (
    <div className={theme}>
      {/* ... so ShippingForm's props will never be the same, and it will re-render every time */}
      <ShippingForm onSubmit={handleSubmit} />
    </div>
  );
}

In javascript, a function () {} or () => {} always creates a different function, similar to how the {} object literal always create new object. Normally, this wouldn’t be a proble, but it means that ShippingForm props will never be the same, and your memo optimization won’t work. This is where useCallback comes in handy.

function ProductPage({ productId, referrer, theme }) {
  // Tell React to cache your function between re-renders...
  const handleSubmit = useCallback((orderDetails) => {
    post('/product/' + productId + '/buy', {
      referrer,
      orderDetails,
    });
  }, [productId, referrer]); // ...so as long as these dependencies don't change...

  return (
    <div className={theme}>
      {/* ...ShippingForm will receive the same props and can skip re-rendering */}
      <ShippingForm onSubmit={handleSubmit} />
    </div>
  );
}

This is all about part 1, waiting for part 2.


About Tinh Tran
Tinh Tran

welcome to my space

Email : thanhtinhpas1@gmail.com

Website : https://thanhtinh.github.io

About thanhtinh

Hi, I'm a fullstack developer, welcome to my space

Star
Useful Links