React

Last Updated: 4/13/2023

Connecting to Backend

Pure Components

  • A component must be pure, meaning:
  • It minds its own business. It should not change any objects or variables that existed before rendering.
  • Same inputs, same output. Given the same inputs, a component should always return the same JSX.
  • Rendering can happen at any time, so components should not depend on each others’ rendering sequence.
  • You should not mutate any of the inputs that your components use for rendering. That includes props, state, and context. To update the screen, “set” state instead of mutating preexisting objects.
  • Strive to express your component’s logic in the JSX you return. When you need to “change things”, you’ll usually want to do it in an event handler. As a last resort, you can useEffect.

useEffect

  • React components should be pure function.
  • A pure function should not have any side effects. Return same result for same input
  • To keep components pure, keep changes out of the render phase
  • Data fetching, setting up a subscription, and manually changing the DOM in React components are all examples of side effects. They’re things that happen on the side, not during rendering.
  • In React, side effects usually belong inside event handlers. Event handlers are functions that React runs when you perform some action—for example, when you click a button. Even though event handlers are defined inside your component, they don’t run during rendering. So event handlers don’t need to be pure.
  • Use the effect hook to perform side effects.
  • Using the effect hook you can execute a piece of code after a component is rendered.
  • Use effect hook at the top level of the components and not inside condition and loops
  • You can call multiple times
  • When you have multiple effects, react will run them in the order after each render
  • The effect hook takes a function that performs the side effect and an optional array of dependencies. Whenever the dependencies change, the effect hook runs again.

Usage

useEffect(() => {
  document.title = "React Sample App";
});

With Dependencies

  • If you update state in useEffect it may run in infinite loop.
  • useEffect takes dependencies as optional parameter
  • To run only once when component is rendered, pass empty array. ie not dependent on any values

Run only once

useEffect(() => {
  document.title = "React Sample App";
}, []);

Run whenever prop or state value changes

useEffect(() => {
  document.title = "React Sample App";
}, [props, state]);

Clean up

  • Can optionally return a function that performs clean up
  • Some effects require cleanup to reduce memory leaks. Timeouts, subscriptions, event listeners, and other effects that are no longer needed should be disposed.
  • To clean up any resources that were created by the effect hook, we can include a cleanup function that runs when the component unmounts or the dependencies change.
useEffect(() => {
	let timer = setTimeout(() => {
		setCount((count) => count + 1);
	}, 1000);

	return () => clearTimeout(timer)
  }, []);

Understanding HTTP Request

  • HyperText Transfer Protocol (HTTP): a protocol for transferring data over the internet.
  • The communication between the front-end and the back-end happens over HTTP, the same protocol that powers the web. The front-end sends an HTTP request to the back-end, and the back-end sends an HTTP response back.
  • Each HTTP request and response contains a header and a body. The header provides metadata about the message, such as the content type and HTTP status code, while the body contains the actual data being sent or received

Axios

  • React is a library for building front-end user interfaces, but to create complete apps, we also need a back-end server to handle business logic, data storage, and other functionality
  • To send HTTP requests to the backend, we can use axios, a popular JavaScript library which makes it easy to send requests
  • Install
npm i axios@1.3.4
  • Import
import axios from "axios";	
  • Set state
const [users, setUsers] = useState([]);
  • Fetch data
useEffect(() => {
    axios
      .get("https://jsonplaceholder.typicode.com/users")
      .then((res) => {
        setUsers(res.data);
      });
  }, []);
  • Render data
return (
    <>
      <ul>
        {users.map((user) => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </>
  );
  • A promise is an object that holds the eventual result or failure of an asynchronous (long running) operation.

Handling Errors

  • Error may occur due to various reasons like network connection drops, server goes down.
  • Handle error using catch and set in state
axios
      .get<User[]>("https://jsonplaceholder.typicode.com/xusers")
      .then((res) => {
        setUsers(res.data);
      })
      .catch((err) => setError(err.message));
  • Display error
{error && <p className="text-danger">{error}</p>}

Using async await

useEffect(() => {
    const fetchUsers = async () => {
      try {
        const res = await axios
        .get<User[]>("https://jsonplaceholder.typicode.com/xusers")  
        setUsers(res.data);
        
      } catch (error) {
        setError((error as AxiosError).message)
      }
    }
    fetchUsers();
  }, []);

Cancelling Request

  • AbortController allows to cancel or abort asynchronous operation like fetch request or DOM manipulation
useEffect(() => {
    const controller = new AbortController();
    axios
      .get<User[]>("https://jsonplaceholder.typicode.com/users", {
        signal: controller.signal,
      })
      .then((res) => {
        setUsers(res.data);
      })
      .catch((err) => {
        if (err instanceof CanceledError) return;
        setError(err.message);
      });

    return () => {
      controller.abort();
    };
  }, []);

Show Loading Indicator

useEffect(() => {
setLoading(true);
axios
      .get<User[]>("https://jsonplaceholder.typicode.com/users", {
        signal: controller.signal,
      })
      .then((res) => {
        setUsers(res.data);
        setLoading(false);
      })
      .catch((err) => {
        if (err instanceof CanceledError) return;
        setError(err.message);
        setLoading(false);
      });
});
<div className="spinner-border"></div>

Deleting Users

  • Optimistic approach - update the UI and then call the server. Preferred
  • Pessimistic approach - call the server and then update the UI
const deleteUser = (user) => {
const originalUsers = [...users];
setUsers(users.filter(u != u.id === user.id))

axios
	.delete("https://jsonplaceholder.typicode.com/users" + user.id)
	.catch(err => {
		setError(err.message);
		setUsers(originalUsers);
	})

Adding User

const addUser = () => {
const originalUsers = [...users];
const newUser = {id:0, name:""};
setUsers([newUser, ...users]);

axios.post(url, newUser)
.then(res => setUsers([res.data, ....users]))
.catch(err => { 
	setError(err.message)
	setUsers(originalUsers)
});

Updating User

  • put: replacing object
  • patch: updating 1 or more properties
	const originalUsers = [...users];
	axios.patch(url + id, updatedUser)
	.catch(err => { 
	setError(err.message)
	setUsers(originalUsers)
});

Custom Hooks

  • Hooks are reusable functions.
  • When you have component logic that needs to be used by multiple components, we can extract that logic to a custom

Defining Hook

const useFetch = (url) => {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch(url)
      .then((res) => res.json())
      .then((data) => setData(data));
  }, [url]);

  return [data];
};

export default useFetch;

Using Hook

const [data] = useFetch("https://jsonplaceholder.typicode.com/todos");