Loading video player...
Now this might surprise you a bit but
ustate is actually built on top of use
reducer. In this lesson we're going to
recreate use state using only use
reducer. It's a fun exercise and by the
end of it you will see how the two hooks
are connected. Let's get started. Back
in VS Code inside the source folder
create a new folder called custom
counter.jsx.
Here scaffold a simple component export
const custom counter is equal to an
arrow function. We return a div that
says custom counter. Now before we write
any logic let's think about what ustate
does for us. It gives us a state value.
It gives us a setter function. And when
we call that setter function, React
updates the state and rerenders the
component. We're going to recreate this
behavior using use reducer inside the
component file. Import the hook. import
use reducer from react. Remember we are
trying to implement use state using use
reducer. Now let's define our own
version of use state. const use state
custom is equal to an arrow function.
And this function will accept an initial
value just like use state does. Next,
let's define the reducer function that
will handle the state updates. With
state, whenever you call set state
passing in a new value, React replaces
the entire state with that new value.
So, our reducer doesn't need to do
anything fancy. It should simply return
the action as the new state. So const
reducer is equal to a function. A
reducer receives state as well as the
action and for use state it simply has
to return the action. Now let's call use
reducer with this reducer function and
the initial value. So use reducer
passing in reducer function and the
initial value. Use reducer returns a
pair of values. First we have state
which holds the current state value.
Then we have dispatch which is a
function we use to send actions to the
reducer. But use state doesn't give us a
dispatch function. It gives us a set
state function. So let's create our own
wrapper. const set state is equal to it
receives a value and we simply call
dispatch passing in that value. Now our
use state custom function can return a
pair of values state and set state just
like the real use state. And there you
go. We have just implemented use state
using use reducer. Let's test it in the
counter component. invoke use state
custom passing in zero as the initial
value. It returns two values and we can
dstructure using the array dstructuring
syntax count, set count. For the JSX we
will render a paragraph with count and
below the paragraph we will render
buttons to increment, decrement and
reset the count value. The first one is
going to be increment with an onclick
handler equal to an arrow function where
we call set count passing in count +
one. I'm going to duplicate this, change
increment to decrement and count minus
one. And finally change increment to
reset and set count to zero. But you can
clearly see that we use state custom
exactly like we would use state. We pass
zero as initial value and dstructure the
returned array into count and set count.
Import this component into app.tjsx.
So import custom counter from dot
/custom counter and render the component
custom counter. Refresh the browser and
test it out. Increment works. Decrement
works reset as well. So far, our version
of use state behaves exactly like the
real use state. But we're not done yet.
Remember that ustate has a second
capability. It lets us pass an updater
function to the setter function. Right
now, our version doesn't support that.
So, let's add it. Now you might be
tempted to handle it inside set state.
If type of new value is equal to
function dispatch
new value of state else dispatch new
value. But there's a problem. We're
using the state variable but that's the
state when set state was created and the
state might become stale in future
renders. A safer solution is to move
this logic into the reducer itself. So
if type of action is equal to a function
return action of state otherwise return
action. Now the reducer has full access
to the latest state during dispatch.
This matches how functional updates work
in React's real use state implementation
as well. Let's test if it works. In the
JSX, add a new button. We'll call this
increment with function. And for the
setter function set count, we pass in
another function. This receives the
previous state as argument. And we
return the previous state + one. In the
browser, refresh. Click increment with
function. And it works exactly like you
state. Now we could simplify our set
state function since it's just calling
dispatch. We could return dispatch
directly. Technically there is no need
for this wrapper function. But using set
state keeps the mental model consistent.
We are setting state not dispatching
actions. So we will keep the wrapper
function. Finally let's handle lazy
initialization just like the real use
state. First we define an initializer
function that checks if the initial
value is a function. So const in it is
equal to an arrow function which
receives the initial value as an
argument. Within the function body we
check if type of initial is equal to a
function. If it is we invoke the
function and return the value with
parenthesis. If it is not a function, we
simply return the passed in value. Now
we pass in it as the third argument to
use reducer. Now our custom state
supports lazy initialization too. For
example, we can define a new state
called data that is an array of 100
zeros. So const data comma set data and
this is going to be equal to use state
custom where we can pass a function as
the initial value. We can have a console
log statement expensive computation and
return array of 100 dot fill zero. In
the browser refresh and we can see the
expensive computation logged once. The
duplicate is because React's strict
mode. But if we clear the console and
increment or decrement, you can see the
lock statement doesn't reappear. It's
one time initialization only. So we have
successfully implemented use state using
use reducer. And guess what? We've just
created our very first custom React
hook. A custom hook is simply a
JavaScript function that starts with the
word use, calls one or more built-in
React hooks, and returns reusable
stateful logic. That's exactly what our
ustate custom function is doing. We will
create many more custom hooks later in
the course and we will even learn how to
structure and organize them properly.
For now, it's enough to understand that
you've already written your first one.
To summarize what we've learned here,
use state is really just a specialized
version of use reducer where the reducer
always returns the action as the new
state. If the action is a function, it
calls it with the current state first
and the dispatch function is wrapped in
a set state function for a nicer API.
Use reducer is the more fundamental
hook. New state is simply a convenience
built on top of it for the most common
use case replacing state with a new
value. Please keep in mind that the
actual React implementation has more
optimizations and edge cases, but the
core idea is the same. In the next
lesson, we'll compare use state and use
reducer more directly and talk about
when you might choose one over the
other.
Github - https://github.com/gopinav/React-19-Tutorials Become a Fullstack Developer with Scrimba - https://scrimba.com/fullstack-path-c0fullstack?via=Codevolution Follow me + Twitter - https://twitter.com/CodevolutionWeb Business - codevolution.business@gmail.com Implementing useState with useReducer