React State Management with the Context API

Component-level state is the state within an individual component. It is only available to that component and only affects that component. We can use hooks such as useState to control component-level state.

But what if you want a piece of state to be available to multiple different components or all of your components? This is what we call application-level state.

Application-level state is the state that is available to the entire application. In order to implement this type of state, we need to implement more complex solutions.

One solution would be to “lift up” the state. This involved moving the piece of state to your App component, the root component which holds all of the other components. While this is a bit simpler to implement than other solutions, the problem with it is that you are giving the App component state that it doesn’t directly need and you will have to pass down the state to each component in a very long-winded way. This is called prop-drilling.

Instead, we can use the Context API as a more dynamic and versatile solution. Here’s an example of how it would be used:

Context File

import React, { useState, createContext } from 'react';

export const MyContext = createContext();

export const MyProvider = props => {
   const [property, setProperty] = useState('Initial state of property');

   return (
      <MyContext.Provider value={[property, setProperty]}>
         {props.children}
      </MyContext.Provider>

App File

import React from 'react';
import { MyProvider } from './MyContext';

function App() {
   return (
      <MyProvider>
         <div>
            <MyComponent />
            Other Components...
         </div>
      </MyProvider>
   );
}

Component File

import React, { useState, useContext } from 'react';
import { MyContext } from './MyContext';

const MyComponent = () => {
   const [property, setProperty] = useContext(MyContext);

   return (
      <h1>{property}</h1>
   );
};

export default MyComponent;

The downside to the Context API is every time we update the code in our Context, all the components that use the context have to re-render.

The benefits are that it’s more simple than Redux and doesn’t require any third-party libraries.

What is React.js?

React.js is a JavaScript library for building user interfaces. It is a front-end web framework. React.js breaks down the user interface into customizable components. It combines pieces of HTML, CSS, and JavaScript in each component. It allows the browser to refresh a single component without having to refresh the entire page. It is one of the most popular and in-demand web frameworks

Most people put all of their body content within a div with the id “root.” This is React convention.

<div id="root">
</div>

Before we can start coding, we must install our dependencies

npm i react react-dom
ReactDOM.render(WHAT TO SHOW, WHERE TO SHOW it, optional callback after render function completed)
var React = require("react");
var ReactDOM = require("react-dom");

ReactDOM.render(<h1>Hello world!</h1>, document.getElementById("root"));

React works by creating these JSX files. HTML is inputted into compiler which converts it into Vanilla JavaScript. The compiler comes from including the React module.

Inside the React Module, there is something called Babel. Babel is a JavaScript compiler. It’s able to take next-gen JavaScript (es6, es7, es8) and compile it down to a version that any browser can understand. This includes compiling JSX into plain JS.

var h1 = document.createElement("h1");
h1.innerHTML = "Hello World!";
document.getElementById("root").appendChild(h1)
import React from "react";
import ReactDOM from "react-dom";

Render method can only take a single HTML element. If you put two back-to-back, it will crash. The way around this is wrapping n elements into a div, so that it’s “1 element.”

ReactDOM.render(
<div>
   <h1>Hello world!</h1>
   <p>This is the first paragraph.</p>
</div>, 
document.getElementById("root")
);
const name = "Joshua";

ReactDOM.render(<h1>Hello {name}!</h1>, document.getElementById("root"));
const num = 4;

ReactDOM.render(<p>Your lucky number is {num}!</p>, document.getElementById("root"));
ReactDOM.render(<p>Your lucky number is {Math.floor(Math.random() * 10)}!</p>, document.getElementById("root"));

You can add any JavaScript value or expression inside the curly braces, but you can’t write JavaScript statements(if statements, etc.)

Styling

External Stylesheets

<h1 class="heading">Title</h1> // no 
<h1 className="heading">Title</h1> // yes

Although it looks like HTML, it’s JSX. It’s being converted to Vanilla JavaScript. There is no “class” property in JS; the correct JS equivalent is the className property.

HTML uses all lowercase. When we write JSX we use camel case because JS uses camel case.

contenteditable vs contentEditable

<h1 contentEditable="true" spellCheck="false">My Favourite Foods</h1>

It is recommended to do all of your styling in an external CSS file then apply those styles to your JSX content with class and id tags.

.heading {
   color: 'red';
}
<h1 className="heading">My Favorite Foods</h1>

Inline Styling

The style property requires a JavaScript object

JavaScript object

{
  key: value,
}
<h1 style="color: 'red'">My Title</h1> // no
<h1 style={{color: 'red'}}>My Title</h1> // yes

Any JavaScript within HTML must be wrapped in curly braces. JavaScript objects must be wrapped in curly braces. Thus, 2 sets of curly braces

const customStyle = {
  color: "red",         // use commas instead of semicolons
  fontSize: "20px",     // font-size becomes fontSize
  border: "1px solid #000" // every value goes in quotes
}

customStyle.color = "blue";  // change styles after definition

ReactDOM.render(
  <div>
    <h1 style={customStyle}>My Favourite Foods</h1>
  </div>,
  document.getElementById("root")
);

React Components

We always capitalize the names of our components because that’s how React differentiates that they are custom components

function Heading() {
   return <h1>My Heading!</h1>
}

ReactDOM.render(
   <div>
      <Heading />
   </div>,
   document.getElementById("root")
);

In the same folder, create a new file called “Heading.jsx”

All of our components separated into individual files with the .jsx extension

import React from "react";

function Heading() {
   return <h1>My Heading!</h1>;
}

export default Heading;

Don’t use parentheses. That will make it run immediately; we want to use it as a component

import React from "react";
import ReactDOM from "react-dom";
import Heading from "./Heading";

Most times, you won’t see any div or child elements in the index.js file. Instead, it will just be a single App custom component.

import React from "react";
import Heading from "./Heading";   // child component
import List from "./List";         // child component

function App(){
   return <div>
      <Heading />
      <List />
   </div>
}

export default App;
ReactDOM.render(
   <App />,
   document.getElementById("root")
);

Create a components folder and put all of your components in it

  • src
    • components
      • App.jsx
      • Heading.jsx
      • List.jsx
  • public
    • index.html
    • styles.css
import App from "./components/App";

State in React

State is a React concept that allows us to make our apps more interactive and mutable.

React components has a built-in state object. 

The user interfaces re-renders based on the value of state variables.

Declarative programming: the use of variables such as state to change an object’s property

Imperative programming: explicitly setting an object’s property

Imperative programming is about how a program works while Declarative programming is about what a program does. 

Using regular variables to declaratively change the user interface of a React component will not work. You need a special kind of variable in order to cause React elements to re-render: State variables. You also need special functions called Hooks that look– or hook— into the state of your app to read and modify it.

You use the useState hook in order to get access to the component’s state.

import React from "react";

function App() {
   const state = React.useState();
}

Whatever we put into the parentheses of the useState function is the initial state of the component.

const state = React.useState(123);

If we wanted to access the state variable we just created, we would extract the first element of the state array:

console.log(state[0])
// prints out 123

Whenever you change state, React updates any UI elements corresponding w/ that piece of state automatically.

Using state[0] to access state variables is kind of unintuitive though and not the most efficient way to code. So we can use destructuring to make it easier on us as programmers:

const [count] = useState(123);

console.log(count);
//prints 123

How do we change the value of state variables?

const [count, setCount] = useState(123);

setCount(count - 1);

console.log(count);
// prints 122