React Hooks
useState
The fundamental of storing information. useState hook allow you to create local state returning you both the value that you can access as well as a setter to modify the value. The modification of the state will trigger a re-rendering of the component.
A simple example of using state would be just a counter with a button that you can click an increment on.
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
useEffect
useEffect replaces the old lifecycle method in react like componentDidMount, componentDidUpdate, componentWillunmount. You can provide a callback method to run whenever the condition triggers. The condition is specify through the second parameter which is called the dependency array.
[]-> Whenever the component is mounted (The component has been loaded into the DOM)no array-> Run after every single render[state]-> Monitor a state so that whenever the state changes the method will be invoked
This is just a more extensive example of on how to use useEffect to trigger the update to displayString to append an extra pipe character whenever you increment by pressing the button. As you can see the button is not directly affecting the displayString but rather through the side effects of the hook.
function Counter() {
let [counter, setCounter] = useState(0);
let [displayString, setDisplayString] = useState("|")
function increment() {
setCounter(counter + 1);
}
useEffect(() => {
setDisplayString(displayString + "|")
}, [counter])
return (
<>
<p>{displayString}</p>
<button onClick={increment}>Increment</button>
</>
)
}
export default function SidePage() {
return (
<div>
<Counter />
</div>
)
}
useContext
useContext solve one of the common problem that you might encounter when using React which is passing states around from the parent to deeply nested children.
Say the state is held by the highest parent and you would need to pass the state to one of the deeply nested children in order for it to display the value properly.
In React the manner of passing props deeply down to one of the child component is called props drilling.
You can see from this example that the Parent had to pass the value as prop through ChildA and then from ChildA to ChildB in order to display the value. Wouldn't it be easier if ChildB was able to just access the value directly from the Parent? With useContext hook you can!
function Parent() {
let [value, setValue] = useState("Display from me nested Child B");
return (
<ChildA display={value} />
)
}
function ChildA({display}) {
return (
<>
<p>Child A</p>
<ChildB display={display} />
</>
)
}
function ChildB({display}) {
return (
<p>{display}</p>
)
}
export default function SidePage() {
return (
<div>
<Parent />
</div>
)
}
In order to leverage useContext you must first create the context through the createContext and then wrap your child component that would like to have access to those states through the context provider.
function Parent() {
let [display, setDisplay] = useState("Display from me nested Child B");
return (
<ParentContext value={display}>
<ChildA />
</ParentContext>
)
}
function ChildA({display}) {
return (
<>
<p>Child A</p>
<ChildB />
</>
)
}
function ChildB() {
let display = useContext(ParentContext);
return (
<p>{display}</p>
)
}
export default function SidePage() {
return (
<div>
<Parent />
</div>
)
}
With the ParentContext.Provider you set the value that you would like to set within context through the value attribute. In this case we are sharing the display state. Now once you wrap the component that you would like to have access to state, all of the children would be able to have access to the context by calling useContext hook.
As you can see within ChildB component we don't need the prop anymore, we can just access it through the context block.
Context can also be nested, if a component is nested in nested context then it will be using the closest context provider for resolving the value.
useRef
useRef is the last of the four most common hook that you will be encountering in React. It is very similar to useState in that they both create a mutable variable that you can use within your component. When you create a reference you can access the value through the current value.
The difference between a state and reference is that, everytime a state is updated through the setter method it will trigger a re-rendering of the component, however, with a reference when you make modification to the current value, it will not trigger a re-render of the component.
An example of creating a reference would be:
function ReferenceTest() {
let counter = useRef(0);
function handleIncrement() {
counter.current++;
console.log(counter.current);
}
return (
<div>
<p>{counter.current}</p>
<button onClick={handleIncrement}>Click me</button>
</div>
)
}
export default function SidePage() {
return (
<div>
<ReferenceTest />
</div>
)
}
You can see in this example, if you were to click on the button, the counter.current value displayed in the HTML page will not be incremented, but if you observe in the console output you will see that the value is indeed being incremented. This is because changing a reference will not trigger a re-rendering of the component.
Practical Usage
One of the common usage of useRef is to point to the real DOM element that's within the HTML and using that as reference you can call DOM methods on it such as focus or select, to focus user's input field to those elements.
function ReferenceTest() {
let nodeReference = useRef(null);
useEffect(() => {
nodeReference.current.focus()
}, [])
return (
<div>
<input ref={nodeReference} defaultValue="Focus on me" />
</div>
)
}
This example component when render will focus the user's texting cursor to the input field when it is loaded. nodeReference.current in this case is pointing to the actual DOM <input> element that's within HTML and therefore that's why you're able to use .focus on it.
The usage of reference comes around when you want to have a mutable variable to store something but you don't want the DOM to react to it. Like an ID or something. Reference also survives re-render as oppose to variables declare with let which is reset to whatever the default value is after re-render.