use `useCallback` only work with `useReducer`? - javascript

I have a very simple todo app built with React.
The App.js looks like this
const App = () => {
const [todos, setTodos] = useState(initialState)
const addTodo = (todo) => {
todo.id = id()
todo.done = false
setTodos([...todos, todo])
}
const toggleDone = (id) => {
setTodos(
todos.map((todo) => {
if (todo.id !== id) return todo
return { ...todo, done: !todo.done }
})
)
}
return (
<div className="App">
<NewTodo onSubmit={addTodo} />
<Todos todos={todos} onStatusChange={toggleDone} />
</div>
)
}
export default App
where <NewTodo> is the component that renders the input form to submit new todo item and <Todos /> is the component that renders the list of the todo items.
Now the problem is that when I toggle/change an existing todo item, the <NewTodo> will get re-rendered since the <App /> gets re-rendered and the prop it passes to <NewTodo>, which is addTodo will also change. Since it is a new <App /> every render the function defined in it will also be a new function.
To fix the problem, I first wrapped <NewTodo> in React.memo so it will skip re-renders when the props didn't change. And I wanted to use useCallback to get a memoized addTodo so that <NewTodo> will not get unnecessary re-renders.
const addTodo = useCallback(
(todo) => {
todo.id = id()
todo.done = false
setTodos([…todos, todo])
},
[todos]
)
But I realized that obviously addTodo is dependent upon todos which is the state that holds the existing todo items and it is changing when you toggle/change an existing todo item. So this memoized function will also change.
Then I switched my app from using useState to useReducer, I found that suddenly my addTodo is not dependent upon the state, at least that's what it looks like to me.
const reducer = (state = [], action) => {
if (action.type === TODO_ADD) {
return [...state, action.payload]
}
if (action.type === TODO_COMPLETE) {
return state.map((todo) => {
if (todo.id !== action.payload.id) return todo
return { ...todo, done: !todo.done }
})
}
return state
}
const App = () => {
const [todos, dispatch] = useReducer(reducer, initialState)
const addTodo = useCallback(
(todo) => {
dispatch({
type: TODO_ADD,
payload: {
id: id(),
done: false,
...todo,
},
})
},
[dispatch]
)
const toggleDone = (id) => {
dispatch({
type: TODO_COMPLETE,
payload: {
id,
},
})
}
return (
<div className="App">
<NewTodo onSubmit={addTodo} />
<Todos todos={todos} onStatusChange={toggleDone} />
</div>
)
}
export default App
As you can see here addTodo is only announcing the action that happens to the state as opposed to doing something directly related to the state. So this would work
const addTodo = useCallback(
(todo) => {
dispatch({
type: TODO_ADD,
payload: {
id: id(),
done: false,
...todo,
},
})
},
[dispatch]
)
My question is, does this mean that useCallback never plays nicely with functions that contain useState? Is this ability to use useCallback to memoize the function considered a benefit of switching from useState to useReducer? If I don't want to switch to useReducer, is there a way to use useCallback with useState in this case?

Yes there is.
You need to use the update function syntax of the setTodos
const addTodo = useCallback(
(todo) => {
todo.id = id()
todo.done = false
setTodos((todos) => […todos, todo])
},
[]
)

You've dived down a bit of a rabbit hole! Your original problem was that your addTodo() function depended on the state todos, therefore whenever todos changed you needed to create a new addTodo function and pass that to NewTodo, causing a re-render.
You discovered useReducer, which could help with this since the reducer is passed the current state, and so does not need to capture it in the closure, so it can be stable over changes of todos. However, the React authors have already thought of this situation, and you don't need useReducer (which is really provided as a concession to those who like the Redux style of state updating!). As Gabriele Petrioli pointed out, you can just use the update usage of the state setter. See the docs.
This allows you to write the callback function that Gabriele has provided.
So to answer your final questions:
does this mean that useCallback always does not play nicely with function that contains useState?
useCallback can play perfectly nicely, but you need to be aware of what you are capturing in the closure you pass to useCallback, and if you are using a variable from useState in your callback you need to pass that variable in the list of deps to ensure that your closure is refreshed and it won't be called with out-of-date state.
And then you have to realize that the callback will be a new function thus causing re-renders to components that take it as an argument.
Is this ability to use useCallback to memoize the function considered a benefit of switching from useState to useReducer?
No, not really. useCallback does not prefer useState or useReducer. As I said, useReducer is really there to support a different style of programming, not because it provides functionality that is not available through other means.
If I don't want to switch to useReducer, is there a way to use useCallback with useState in this case?
Yes, as outlined above.

As mentioned by Gabriele Petrioli, You can use the callback syntax or you can keep the value of dependencies of your callback in a ref and use that ref in your callback instead of the state as mentioned here.
In your example, this approach would look something like this:
const [todos, setTodos] = useState([]);
const todosRef = useRef(todos);
useEffect(() => {
todosRef.current = todos;
},[todos]);
const addTodo = useCallback(
todo => {
todo.id = id()
todo.done = false
setTodos([…todosRef.current, todo])
},
[todosRef]
)

Related

Functional component with React.memo() still rerenders

I have a button component that has a button inside that has a state passed to it isActive and a click function. When the button is clicked, the isActive flag will change and depending on that, the app will fetch some data. The button's parent component does not rerender. I have searched on how to force stop rerendering for a component and found that React.memo(YourComponent) must do the job but still does not work in my case. It also make sense to pass a check function for the memo function whether to rerender or not which I would set to false all the time but I cannot pass another argument to the function. Help.
button.tsx
interface Props {
isActive: boolean;
onClick: () => void;
}
const StatsButton: React.FC<Props> = ({ isActive, onClick }) => {
useEffect(() => {
console.log('RERENDER');
}, []);
return (
<S.Button onClick={onClick} isActive={isActive}>
{isActive ? 'Daily stats' : 'All time stats'}
</S.Button>
);
};
export default React.memo(StatsButton);
parent.tsx
const DashboardPage: React.FC = () => {
const {
fetchDailyData,
fetchAllTimeData,
} = useDashboard();
useEffect(() => {
fetchCountry();
fetchAllTimeData();
// eslint-disable-next-line
}, []);
const handleClick = useEventCallback(() => {
if (!statsButtonActive) {
fetchDailyData();
} else {
fetchAllTimeData();
}
setStatsButtonActive(!statsButtonActive);
});
return (
<S.Container>
<S.Header>
<StatsButton
onClick={handleClick}
isActive={statsButtonActive}
/>
</S.Header>
</S.Container>
)
}
fetch functions are using useCallback
export const useDashboard = (): Readonly<DashboardOperators> => {
const dispatch: any = useDispatch();
const fetchAllTimeData = useCallback(() => {
return dispatch(fetchAllTimeDataAction());
}, [dispatch]);
const fetchDailyData = useCallback(() => {
return dispatch(fetchDailyDataAction());
}, [dispatch]);
return {
fetchAllTimeData,
fetchDailyData,
} as const;
};
You haven't posted all of parent.tsx, but I assume that handleClick is created within the body of the parent component. Because the identity of the function will be different on each rendering of the parent, that causes useMemo to see the props as having changed, so it will be re-rendered.
Depending on if what's referenced in that function is static, you may be able to use useCallback to pass the same function reference to the component on each render.
Note that there is an RFC for something even better than useCallback; if useCallback doesn't work for you look at how useEvent is defined for an idea of how to make a better static function reference. It looks like that was even published as a new use-event-callback package.
Update:
It sounds like useCallback won't work for you, presumably because the referenced variables used by the callback change on each render, causing useCallback to return different values, thus making the prop different and busting the cache used by useMemo. Try that useEventCallback approach. Just to illustrate how it all works, here's a naive implementation.
function useEventCallback(fn) {
const realFn = useRef(fn);
useEffect(() => {
realFn.current = fn;
}, [fn]);
return useMemo((...args) => {
realFn.current(...args)
}, []);
}
This useEventCallback always returns the same memoized function, so you'll pass the same value to your props and not cause a re-render. However, when the function is called it calls the version of the function passed into useEventCallback instead. You'd use it like this in your parent component:
const handleClick = useEventCallback(() => {
if (!statsButtonActive) {
fetchDailyData();
} else {
fetchAllTimeData();
}
setStatsButtonActive(!statsButtonActive);
});

Converting componentDidMount into useEffect

I am trying to learn React hooks, and trying to convert existing codebase to use hooks, but I am confused.
Is it normal to set state inside useEffect? Would I be causing the dreaded infinite loop if I do so?
import React, { useState, useEffect } from 'react';
import App from 'next/app';
import Layout from './../components/Layout';
function MyApp({ Component, pageProps }) {
const [ cart, setCart ] = useState([]);
const addToCart = (product) => {
setCart((prevCartState) => {
return [ ...prevCartState, product ];
});
localStorage.setItem('cart', JSON.stringify(cart));
};
//mimicking componentDidMount to run some effect using useEffect
//useEffect(() => setCount((currentCount) => currentCount + 1), []);
useEffect(() => {
const cartCache = JSON.parse(localStorage.getItem('cart'));
//cart = cartCache; Good or bad?
cartCache || setCart(() =>{
});
}, []);
return <Component {...pageProps} />;
}
My original class based component:
/*
export default class MyApp extends App {
state = {
cart: []
}
componentDidMount = () => {
const cart = JSON.parse(localStorage.getItem('cart'));
if (cart) {
this.setState({
cart
});
}
};
addToCart = (product) => {
this.setState({
cart: [...this.state.cart, product]
});
localStorage.setItem('cart', JSON.stringify(this.state.cart));
}
render() {
const { Component, pageProps } = this.props
return (
<contextCart.Provider value={{ cart: this.state.cart, addToCart: this.addToCart }}>
<Layout>
<Component {...pageProps} />
</Layout>
</contextCart.Provider>
)
}
}*/
It's okey to set state inside useEffect as long as you don't listen to changes of the same field inside dependency array. In your particular case you are calling useEffect only once (since you have passed an empty dependency array).
useEffect(() => {
const cartCache = JSON.parse(localStorage.getItem('cart'));
if (cartCache) {
setCart(cartCache);
}
}, []);
Also would be cool to add the second useEffect to listen to cart changes and keep the localStorage up-to-date.
useEffect(() => {
localStorage.setItem('cart', JSON.stringify(cart));
}, [cart]);
Because localStorage.getItem() is a synchronous call thus for this scenario you can also use the function callback version of useState in order to set initial value. In this way you don't need to use useEffect. Usually if it is possible I try to avoid introducing any new side effects in my functional components.
You can try as the following instead:
const [ cart, setCart ] = useState(() => {
const cartCache = JSON.parse(localStorage.getItem('cart'));
if (cartCache) {
return cartCache;
}
localStorage.setItem('cart', JSON.stringify([]));
return [];
});
In this way if the cart element is missing from the localStorage the code will create it with a default [] empty array and set it also for your state. Other case it will set your state the value from storage.
Please note: Also I agree with the answer from kind user here in terms of listening cart state changes to keep localStorage up to date with useEffect. My suggestion is only for the initial state.

React hooks state Management

I'm using hooks in my react project.
I have written a function to manage the state in hooks.
but I don't know if it is Optimal.
This is my function:
mapStateToDOM.js
import {useState} from 'react';
const mspStateToDOM = (initialState, state) => {
Object.keys(initialState).map(item => {
let [current, setCurrent] = useState(initialState[item]);
state[item] = {get: () => current, set: setCurrent};
});
};
export default mspStateToDOM;
And with it, I can get a variable from state by using state.varName.get()
And I can change a variable by using state.varName.set(SOME_VALUE)
Here is my code:
import React from 'react';
import mspStateToDOM from '../config/mapStateToDOM/mapStateToDOM';
const Counter = () => {
const state = {};
const initialState = {
count: 5,
count2: 6
};
mspStateToDOM(initialState, state);
return (
<div>
<p>You clicked {state.count.get()} times</p>
<button
onClick={() => {
state.count.set(state.count.get() + 1);
}}>
Click Me!
</button>
</div>
)
};
export default Counter;
This makes the code much cleaner, readable, and easier to use. and I don't have to define a setter for each variable and use 'useState' many times in my code.
but I don't know if it is optimal. Is it?
You can't use hooks inside a loop\callback as stated in Rules of Hooks.
Therefore, such code is error-prone, the only reason you don't get a warning as the linter can't guess it's a custom hook.
Try adding use prefix to your custom hook name and see the warning:
React Hook useState cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function. (react-hooks/rules-of-hooks)
const useMapStateToProps = (initialState, state) => {
Object.keys(initialState).map(item => {
let [current, setCurrent] = useState(initialState[item]);
state[item] = { get: () => current, set: setCurrent };
});
};
Moreover, there are few bugs in this snippet:
state and initialState will re-assign on every render.
Use of map without a return value.
Rules of Hooks as stated above.
Not a bug but in those snippets, it seems you trying to use OOP approach which comes from classes, in a function, while hooks motivate you for functional programming.
Refer to Using Custom Hooks
To add to what has been said by Dennis Vash.
This line: state.count.set(state.count.get() + 1) can easily be replaced by state.count.set(prevState => { return prevState + 1 })
Also, I suggest doing
const state = {};
const initialState = {
count: 5,
count2: 6
};
const Counter = () => {
const [state, setState] = useState(initialState);
return (
<div>
<p>You clicked {state.count} times</p>
<button
onClick={() => {
setState(prevState => ({ ...prevState, count: prevState.count + 1 }) );
}}>
Click Me!
</button>
</div>
)
};
No need to overcomplicate things.
But what's with count2? It hasn't been used anywhere.

componentWillUnmount with React useEffect hook

How can the useEffect hook (or any other hook for that matter) be used to replicate componentWillUnmount?
In a traditional class component I would do something like this:
class Effect extends React.PureComponent {
componentDidMount() { console.log("MOUNT", this.props); }
componentWillUnmount() { console.log("UNMOUNT", this.props); }
render() { return null; }
}
With the useEffect hook:
function Effect(props) {
React.useEffect(() => {
console.log("MOUNT", props);
return () => console.log("UNMOUNT", props)
}, []);
return null;
}
(Full example: https://codesandbox.io/s/2oo7zqzx1n)
This does not work, since the "cleanup" function returned in useEffect captures the props as they were during mount and not state of the props during unmount.
How could I get the latest version of the props in useEffect clean up without running the function body (or cleanup) on every prop change?
A similar question does not address the part of having access to the latest props.
The react docs state:
If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array ([]) as a second argument. This tells React that your effect doesn’t depend on any values from props or state, so it never needs to re-run.
In this case however I depend on the props... but only for the cleanup part...
You can make use of useRef and store the props to be used within a closure such as render useEffect return callback method
function Home(props) {
const val = React.useRef();
React.useEffect(
() => {
val.current = props;
},
[props]
);
React.useEffect(() => {
return () => {
console.log(props, val.current);
};
}, []);
return <div>Home</div>;
}
DEMO
However a better way is to pass on the second argument to useEffect so that the cleanup and initialisation happens on any change of desired props
React.useEffect(() => {
return () => {
console.log(props.current);
};
}, [props.current]);
useLayoutEffect() is your answer in 2021
useLayoutEffect(() => {
return () => {
// Your code here.
}
}, [])
This is equivalent to ComponentWillUnmount.
99% of the time you want to use useEffect, but if you want to perform any actions before unmounting the DOM then you can use the code I provided.
useLayoutEffect is great for cleaning eventListeners on DOM nodes.
Otherwise, with regular useEffect ref.current will be null on time hook triggered
More on react docs https://reactjs.org/docs/hooks-reference.html#uselayouteffect
import React, { useLayoutEffect, useRef } from 'react';
const audioRef = useRef(null);
useLayoutEffect(() => {
if (!audioRef.current) return;
const progressEvent = (e) => {
setProgress(audioRef.current.currentTime);
};
audioRef.current.addEventListener('timeupdate', progressEvent);
return () => {
try {
audioRef.current.removeEventListener('timeupdate', progressEvent);
} catch (e) {
console.warn('could not removeEventListener on timeupdate');
}
};
}, [audioRef.current]);
Attach ref to component DOM node
<audio ref={audioRef} />
useEffect(() => {
if (elements) {
const cardNumberElement =
elements.getElement('cardNumber') || // check if we already created an element
elements.create('cardNumber', defaultInputStyles); // create if we did not
cardNumberElement.mount('#numberInput');
}
}, [elements]);

How to call a componentDidMount in the functional component [duplicate]

Instead of writing my components inside a class, I'd like to use the function syntax.
How do I override componentDidMount, componentWillMount inside function components?
Is it even possible?
const grid = (props) => {
console.log(props);
let {skuRules} = props;
const componentDidMount = () => {
if(!props.fetched) {
props.fetchRules();
}
console.log('mount it!');
};
return(
<Content title="Promotions" breadcrumbs={breadcrumbs} fetched={skuRules.fetched}>
<Box title="Sku Promotion">
<ActionButtons buttons={actionButtons} />
<SkuRuleGrid
data={skuRules.payload}
fetch={props.fetchSkuRules}
/>
</Box>
</Content>
)
}
Edit: With the introduction of Hooks it is possible to implement a lifecycle kind of behavior as well as the state in the functional Components. Currently
Hooks are a new feature proposal that lets you use state and other
React features without writing a class. They are released in React as a part of v16.8.0
useEffect hook can be used to replicate lifecycle behavior, and useState can be used to store state in a function component.
Basic syntax:
useEffect(callbackFunction, [dependentProps]) => cleanupFunction
You can implement your use case in hooks like
const grid = (props) => {
console.log(props);
let {skuRules} = props;
useEffect(() => {
if(!props.fetched) {
props.fetchRules();
}
console.log('mount it!');
}, []); // passing an empty array as second argument triggers the callback in useEffect only after the initial render thus replicating `componentDidMount` lifecycle behaviour
return(
<Content title="Promotions" breadcrumbs={breadcrumbs} fetched={skuRules.fetched}>
<Box title="Sku Promotion">
<ActionButtons buttons={actionButtons} />
<SkuRuleGrid
data={skuRules.payload}
fetch={props.fetchSkuRules}
/>
</Box>
</Content>
)
}
useEffect can also return a function that will be run when the component is unmounted. This can be used to unsubscribe to listeners, replicating the behavior of componentWillUnmount:
Eg: componentWillUnmount
useEffect(() => {
window.addEventListener('unhandledRejection', handler);
return () => {
window.removeEventListener('unhandledRejection', handler);
}
}, [])
To make useEffect conditional on specific events, you may provide it with an array of values to check for changes:
Eg: componentDidUpdate
componentDidUpdate(prevProps, prevState) {
const { counter } = this.props;
if (this.props.counter !== prevState.counter) {
// some action here
}
}
Hooks Equivalent
useEffect(() => {
// action here
}, [props.counter]); // checks for changes in the values in this array
If you include this array, make sure to include all values from the component scope that change over time (props, state), or you may end up referencing values from previous renders.
There are some subtleties to using useEffect; check out the API Here.
Before v16.7.0
The property of function components is that they don't have access to Reacts lifecycle functions or the this keyword. You need to extend the React.Component class if you want to use the lifecycle function.
class Grid extends React.Component {
constructor(props) {
super(props)
}
componentDidMount () {
if(!this.props.fetched) {
this.props.fetchRules();
}
console.log('mount it!');
}
render() {
return(
<Content title="Promotions" breadcrumbs={breadcrumbs} fetched={skuRules.fetched}>
<Box title="Sku Promotion">
<ActionButtons buttons={actionButtons} />
<SkuRuleGrid
data={skuRules.payload}
fetch={props.fetchSkuRules}
/>
</Box>
</Content>
)
}
}
Function components are useful when you only want to render your Component without the need of extra logic.
You can use react-pure-lifecycle to add lifecycle functions to functional components.
Example:
import React, { Component } from 'react';
import lifecycle from 'react-pure-lifecycle';
const methods = {
componentDidMount(props) {
console.log('I mounted! Here are my props: ', props);
}
};
const Channels = props => (
<h1>Hello</h1>
)
export default lifecycle(methods)(Channels);
You can make your own "lifecycle methods" using hooks for maximum nostalgia.
Utility functions:
import { useEffect, useRef } from "react";
export const useComponentDidMount = handler => {
return useEffect(() => handler(), []);
};
export const useComponentDidUpdate = (handler, deps) => {
const isInitialMount = useRef(true);
useEffect(() => {
if (isInitialMount.current) {
isInitialMount.current = false;
return;
}
return handler();
}, deps);
};
export const useComponentWillUnmount = handler => {
return useEffect(() => handler, []);
};
Usage:
import {
useComponentDidMount,
useComponentDidUpdate,
useComponentWillUnmount
} from "./utils";
export const MyComponent = ({ myProp }) => {
useComponentDidMount(() => {
console.log("Component did mount!");
});
useComponentDidUpdate(() => {
console.log("Component did update!");
});
useComponentDidUpdate(() => {
console.log("myProp did update!");
}, [myProp]);
useComponentWillUnmount(() => {
console.log("Component will unmount!");
});
return <div>Hello world</div>;
};
Solution One:
You can use new react HOOKS API. Currently in React v16.8.0
Hooks let you use more of React’s features without classes.
Hooks provide a more direct API to the React concepts you already know: props, state, context, refs, and lifecycle.
Hooks solves all the problems addressed with Recompose.
A Note from the Author of recompose (acdlite, Oct 25 2018):
Hi! I created Recompose about three years ago. About a year after
that, I joined the React team. Today, we announced a proposal for
Hooks. Hooks solves all the problems I attempted to address with
Recompose three years ago, and more on top of that. I will be
discontinuing active maintenance of this package (excluding perhaps
bugfixes or patches for compatibility with future React releases), and
recommending that people use Hooks instead. Your existing code with
Recompose will still work, just don't expect any new features.
Solution Two:
If you are using react version that does not support hooks, no worries, use recompose(A React utility belt for function components and higher-order components.) instead. You can use recompose for attaching lifecycle hooks, state, handlers etc to a function component.
Here’s a render-less component that attaches lifecycle methods via the lifecycle HOC (from recompose).
// taken from https://gist.github.com/tsnieman/056af4bb9e87748c514d#file-auth-js-L33
function RenderlessComponent() {
return null;
}
export default lifecycle({
componentDidMount() {
const { checkIfAuthed } = this.props;
// Do they have an active session? ("Remember me")
checkIfAuthed();
},
componentWillReceiveProps(nextProps) {
const {
loadUser,
} = this.props;
// Various 'indicators'..
const becameAuthed = (!(this.props.auth) && nextProps.auth);
const isCurrentUser = (this.props.currentUser !== null);
if (becameAuthed) {
loadUser(nextProps.auth.uid);
}
const shouldSetCurrentUser = (!isCurrentUser && nextProps.auth);
if (shouldSetCurrentUser) {
const currentUser = nextProps.users[nextProps.auth.uid];
if (currentUser) {
this.props.setCurrentUser({
'id': nextProps.auth.uid,
...currentUser,
});
}
}
}
})(RenderlessComponent);
componentDidMount
useEffect(()=>{
// code here
})
componentWillMount
useEffect(()=>{
return ()=>{
//code here
}
})
componentDidUpdate
useEffect(()=>{
//code here
// when userName state change it will call
},[userName])
According to the documentation:
import React, { useState, useEffect } from 'react'
// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
});
see React documentation
Short and sweet answer
componentDidMount
useEffect(()=>{
// code here
})
componentWillUnmount
useEffect(()=>{
return ()=>{
//code here
}
})
componentDidUpdate
useEffect(()=>{
//code here
// when userName state change it will call
},[userName])
You can make use of create-react-class module.
Official documentation
Of course you must first install it
npm install create-react-class
Here is a working example
import React from "react";
import ReactDOM from "react-dom"
let createReactClass = require('create-react-class')
let Clock = createReactClass({
getInitialState:function(){
return {date:new Date()}
},
render:function(){
return (
<h1>{this.state.date.toLocaleTimeString()}</h1>
)
},
componentDidMount:function(){
this.timerId = setInterval(()=>this.setState({date:new Date()}),1000)
},
componentWillUnmount:function(){
clearInterval(this.timerId)
}
})
ReactDOM.render(
<Clock/>,
document.getElementById('root')
)
if you using react 16.8 you can use react Hooks...
React Hooks are functions that let you “hook into” React state and lifecycle features from function components...
docs
import React, { useState, useEffect } from "react";
const Counter = () => {
const [count, setCount] = useState(0);
const [count2, setCount2] = useState(0);
// componentDidMount
useEffect(() => {
console.log("The use effect ran");
}, []);
// // componentDidUpdate
useEffect(() => {
console.log("The use effect ran");
}, [count, count2]);
// componentWillUnmount
useEffect(() => {
console.log("The use effect ran");
return () => {
console.log("the return is being ran");
};
}, []);
useEffect(() => {
console.log(`The count has updated to ${count}`);
return () => {
console.log(`we are in the cleanup - the count is ${count}`);
};
}, [count]);
return (
<div>
<h6> Counter </h6>
<p> current count: {count} </p>
<button onClick={() => setCount(count + 1)}>increment the count</button>
<button onClick={() => setCount2(count2 + 1)}>increment count 2</button>
</div>
);
};
export default Counter;

Categories