useState | useEffect without setting new state casues additional rerender - javascript

I couldn't find any information why this render is called twice ?
const Test: React.FC = () => {
const [myState, setMyState] = useState();
console.log("RENDER TEST");
return <div>test</div>;
};
When I remove
const [myState, setMyState] = useState();
then the component is rendered only once.
The same happens with useEffect:
const Test: React.FC = () => {
useEffect(() => {
console.log("Component mounted");
}, []);
console.log("RENDER TEST");
return <div>test</div>;
};
Without useEffect the render is called only once.

It is happening due to React's StrictMode in order to detect any problems and to want you about them. It runs only in development and not in production. In your application in index.js you will find that your App component is wrapped with React.StrictMode component. You can read more about it https://reactjs.org/docs/strict-mode.html

Related

Multiple execution of a function

export default function MyQuestions() {
const router = useRouter();
const [auth, setAuth] = useState(false);
const checkAuth = async () => {
const loggedInUsername = await getUsername();
if (router.query.username === loggedInUsername) return setAuth(true);
return;
};
checkAuth();
This is a part of a React component where I execute the checkAuth function. I thought it should execute only once but that is not the case. It is executed 4 times and if I remove the returns it is executed even more than 10 times and I don't understand why. In js a function that reaches the end should stop automatically.
Why does this happen?
In this code router is of Next.js
What are the conditions under which the check should be re-run? This is what useEffect is intended for. useEffect accepts a function to run the desired effect, and a list of dependencies to specify when an effect should be run -
import { useRouter } from ...
import { useEffect, useState } from "react"
function MyQuestions() {
const router = useRouter()
const [auth, setAuth] = useState(false)
useEffect(async () => {
const loggedInUsername = await getUsername()
if (router.query.username === loggedInUsername)
setAuth(true)
}, [getUsername, router.query.username, setAuth])
return <>...</>
}
Any free variable inside the effect must be listed as a dependency of the effect. There's one issue however. setAuth will be a new function each time MyQuestions is rendered. To ensure setAuth will be the same for each render, we can use useCallback -
import { useRouter } from ...
import { useEffect, useCallback, useState } from "react"
function MyQuestions() {
const router = useRouter()
const [auth, setAuth] = useState(false)
const authenticate =
useCallback(_ => setAuth(true), [])
useEffect(async () => {
const loggedInUsername = await getUsername()
if (router.query.username === loggedInUsername)
authenticate()
}, [getUsername, router.query.username, authenticate])
return <>...</>
}
Now the effect will only re-run when getUsername, router.query.username or authenticate changes. Considering getUsername and authenticate are functions and should not change, we can expect that the effect will only re-run when router.query.username changes.
I haven't used nextjs but i suppose it happens because it is executed on every render of the router component.
If you want to use it once, just call it in a use effect when the component mounts.
useEffect(() => {
checkAuth();
}, []) // This will run once, when the component mounts
There is no need to return the setState call:
const checkAuth = async () => {
const loggedInUsername = await getUsername();
if (router.query.username === loggedInUsername) setAuth(true);
};
Also because you are calling the checkAuth() function right after you call it. Setting state in React causes a re-render. So the reason it is executed 4 to 10 times is because your MyQuestion component re-renders when you setState(), and when it rerenders, is hits the checkAuth() function again and the cycle repeats.
As mentioned put the functionality in a useEffect() with an empty dependency array:
useEffect(() => {
const loggedInUsername = await getUsername();
if (router.query.username === loggedInUsername) return setAuth(true);
}, [])

Redux and ReactJS Hooks: UseEffect Returns an Empty State

I have this class component that returns state populated perfectly using Redux store:
class TopRatedComponent extends Component {
componentDidMount() {
this.props.fetchTopRatedMovies();
}
render() {
const IMG_API_ROOT_LINK = 'https://image.tmdb.org/t/p/w500';
const { topRatedMovies, loading, error } = this.props;
return (
<div>
{loading && <div>LOADING...</div>}
{error && <div>{error}</div>}
<Container className="p-4" onScroll={this.onScroll}>
<div className="text-center">
{
topRatedMovies.results && topRatedMovies.results.map(topRated => (
<p>{topRated.title}</p>
))
}
</div>
</Container>
</div>
);
}
}
const mapStateToProps = state => {
const { topRatedMovies, loading, error } = state.topRatedMovies;
return {
topRatedMovies,
loading,
error
};
};
export default connect(
mapStateToProps,
{
fetchTopRatedMovies
}
)(TopRatedComponent);
However, when I switch the above class component into a functional component below so I can use ReactJS hooks with my code, but the state is always empty.
const TopRatedComponent = () => {
const [count, setCount] = useState(0);
const [topRated, settopRated] = useState([]);
useEffect(() => {
settopRated(this.props.fetchTopRatedMovies)
}, [this.props.fetchTopRatedMovies])
const allState = useSelector((state) => state)
console.log('CHOF: ' + JSON.stringify(allState));
return (
<div>
WOOOOOOOW....
</div>
)
};
You haven't correctly transformed your class-based component into equivalent functional component.
Following are the problems in your functional component:
In class component, you receive the fetchTopRatedMovies action creator as a prop and you dispatch it from the componentDidMount lifecycle method. In functional component, you are not dispatching it.
To dispatch the action in functional components, use useDispatch() hook and use useEffect hook to dispatch this action after component has mounted.
import React, { useEffect } from "react";
import { useDispatch } from "react-redux";
const TopRatedComponent = () => {
...
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchTopRatedMovies());
}, []);
...
};
In class components, you access props object using this but in functional components, props object is passed as an argument. So, you can directly access it using the parameter name you use for the props object
const TopRatedComponent = (props) => {
console.log(props);
// code
};
Data that your component receives as props from the redux store using mapStateToProps function and connect higher order component, can be accessed using useSelector hook in functional components.
import { useSelector } from "react-redux";
const TopRatedComponent = () => {
const {
topRatedMovies,
loading,
error
} = useSelector(state => state.topRatedMovies);
// code
};
Note: You can also use connect higher-order component and mapStateToProps to connect your functional component with the redux store.
For details of how to use hooks with react-redux, see: react-redux - Hooks
this doesn't work the same way in a functional component. You need to pull props from your function arguments instead.
const TopRatedComponent = (props) => {
const [count, setCount] = useState(0);
const [topRated, settopRated] = useState([]);
useEffect(() => {
settopRated(props.fetchTopRatedMovies)
}, [props.fetchTopRatedMovies])
const allState = useSelector((state) => state)
console.log('CHOF: ' + JSON.stringify(allState));
return (
<div>
WOOOOOOOW....
</div>
)
};
Arrow functions don't have their own context (this variable). I assume that fetchTopRatedMovies is an action (thunk) that fetches data from an API and set it to a global state. In this case, you need to also get that data using Redux hooks (if the version you are using supports it).
const TopRatedComponent = ({ fetchTopRatedMovies }) => {
const [count, setCount] = useState(0);
// this is written according to your `mapStateToProps` function.
const { topRatedMovies, loading, error } = useSelector(({topRatedMovies}) => topRatedMovies);
useEffect(() => {
fetchTopRatedMovies();
}, [fetchTopRatedMovies])
if (loading) return 'LOADING...';
if (error) return <div>{error.message}</div>
return (
<div>
etc...
</div>
)
};

React custom hooks in callback

I'm trying to use my custom hook inside the callback logic like this:
import React, { useEffect, useState } from 'react';
import useDataChange from '../../hooks/useDataChange';
const SomeComponent = () => {
return (
<Table
handleTableChange={data => useDataChange(data)}
/>
);
};
export default SomeComponent;
And my custom hooks (just to simplify) looks like that:
const useDataChange = data => {
console.log(data);
};
export default useDataChange;
In short, custom hook supposed to be fired when data from table is changed (ie. when handleTableChange in Table component is fired). Instead I'm getting:
React Hook "useDataChange" 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
How can I use it when table data is changed?
The key to understanding hooks is to extract pieces of react code out of components. So your first step would be to get it working inside the component
const SomeComponent = () => {
const [data, setData] = useState([])
return (
<Table
handleTableChange={setData}
/>
);
};
Based on your code, I'm not seeing where you'd need a hook or side effect. But let's pretend that you do want to run some simple side effect:
const SomeComponent = () => {
const [data, setData] = useState([])
const [modifiedData, setModifiedData] = useState([])
useEffect(() => {
//here we're just going to save the current data stream into a new state variable for simplicity
setModifiedData(data)
}, [data])
return (
<Table
handleTableChange={setData}
data={modifiedData}
/>
);
};
So now we have some logic that runs a side effect. Now you can extract it to its own hook.
const useModifiedData = (data) => {
const [modifiedData, setModifiedData] = useState(data)
useEffect(() => {
setModifiedData(data)
}, [data])
return modifiedData
}
const SomeComponent = () => {
const [data, setData] = useState([])
const modifiedData = useModifiedData(data)
return (
<Table
handleTableChange={setData}
data={modifiedData}
/>
);
};
Here you have a hook that lives outside the component logic, so it can now go in its own file and be used across your project.
Like it says React Hooks must be called in a React function component or a custom React Hook function react-hooks/rules-of-hooks. React has this limitation so that it can track the state and effects. In your case you can define you custom hook to return a function which does the desired work, instead of directly doing it in your hook.
In this case your custom hook file will look something like this-
const useDataChange = () => data => {
console.log(data);
};
export default useDataChange;
Then in your component you can use it like this -
import React, { useEffect, useState } from 'react';
import useDataChange from '../../hooks/useDataChange';
const SomeComponent = () => {
const callback = useDataChnage();
return (
<Table handleTableChange={callbackdata} />
);
};
export default SomeComponent;

React Hook useEffect has a missing dependency

I am having this problem to build my app. Anyone knows what is wrong?
React Hook useEffect has a missing dependency: 'conectar'. Either include it or remove the dependency array react-hooks/exhaustive-deps
const GraficoEquivalenteNovo = props => {
const [equivalenteNovos, setEquivalenteNovos] = useState([]);
const [data, setData] = useState([]);
async function conectar() {
const resposta = await ConexaoGraficoEquivalenteNovo(props);
setEquivalenteNovos(resposta[0]);
setData(resposta[1]);
}
useEffect(() => {
conectar();
}, [props]);
return (....)
};
Your hook depends on the function connectar which is declared outside the hook, but is internal to the render process. It is re-manufactured on every render. Therefore, React sees it as a volatile dependency. You could have the function outside your component but since the function itself uses state hooks and depends on props, move it into the effect hook.
useEffect(() => {
async function conectar() {
const resposta = await ConexaoGraficoEquivalenteNovo(props);
setEquivalenteNovos(resposta[0]);
setData(resposta[1]);
}
conectar();
}, [props]);

Can I use useReducer from outside component

Now I'm trying to use useReducer to created a new way for management state and function but now found the problem is "Hooks can only be called inside of the body of a function component"
Is there any way to solve this problem?
// App Component
import React from "react";
import { product, productDis } from "./ProductReducer";
//{product} is state, {productDis} is dispatch
import { total } from "./TotalReducer";
//{total} is state and i dont need {totalDis}
const App = () => {
return (
<div>
<button onClick={()=>productDis({type:'add',payload:'pen'})}>add</button>
{product} {total}
</div>
);
};
export default App;
// ProductReducer Component
import React, { useReducer } from 'react';
import {totalDis} from './TotalReducer'
//{totalDis} is dispatch and i dont need {total}
export const [product, productDis] = useReducer((state, action) => {
switch (action.type) {
case "add": {
const product_0 = 'pencil'
const product_1 = `${action.payload} and ${product_0}`
totalDis({
type:'total_add',
payload:'250'
})
return product_1;
}
default:
return state;
}
}, []);
// TotalReducer Component
import React, { useReducer } from 'react';
export const [total, totalDis] = useReducer((total, action) => {
switch (action.type) {
case "total_add": {
const vat = action.payload*1.15
return vat;
}
default:
return total;
}
}, 0)
when i click the button on display It should be shown..." pen and pencil 287.5 "
but it show "Hooks can only be called inside of the body of a function component"
there any way to solve this problem? or i should back to nature?
React hooks should be called only inside functional components. Hook state is maintained per component instance. If hooks have to be reused, they can be extracted into custom hooks, which are functions that call built-in hooks and are supposed to be called inside functional components:
export const useTotal = () => {
const [total, totalDis] = useReducer((total, action) => {...}, 0);
...
return [total, totalDis];
};
In case there's a need to maintain common state for multiple components it should be maintained in common parent and be provided to children through props:
const Root = () => (
const [total, totalDispatcher] = useTotal();
return <App {...{total, totalDispatcher}}/>
);
const App = props => {
return (
<div>{props.total}</div>
);
};
Or context API:
const TotalContext = createContext();
const Root = () => (
<TotalContext.Provider value={useTotal()}>
<App/>
</TotalContext.Provider>
);
const App = () => {
const [total] = useContext(TotalContext);
return (
<div>{total}</div>
);
};
With useEnhancedReducer hook introduced here which returns getState function.
You will have something like.
const [state, dispatch, getState] = useEnahancedReducer(reducer, initState)
Because dispatch, getState will never change, they can be used in some hooks without their appearance in the dependence list, they can be stored somewhere else (outside of react) to to be called at anytime, from anywhere.
There is also version of useEnhancedReducer which supports adding middleware, in the same article.
From the docs,
There are three common reasons you might be seeing it:
You might have mismatching versions of React and React DOM.
You might be breaking the Rules of Hooks.
You might have more than one copy of React in the same app.
Deep drive to the docs. I hope, you'll be able to resolve the issue. Especially see:
Breaking the Rules of Hooks:
function Counter() {
// ✅ Good: top-level in a function component
const [count, setCount] = useState(0);
// ...
}
function useWindowWidth() {
// ✅ Good: top-level in a custom Hook
const [width, setWidth] = useState(window.innerWidth);
// ...
}
If you break these rules, you might see this error.
function Bad1() {
function handleClick() {
// 🔴 Bad: inside an event handler (to fix, move it outside!)
const theme = useContext(ThemeContext);
}
// ...
}
function Bad2() {
const style = useMemo(() => {
// 🔴 Bad: inside useMemo (to fix, move it outside!)
const theme = useContext(ThemeContext);
return createStyle(theme);
});
// ...
}
class Bad3 extends React.Component {
render() {
// 🔴 Bad: inside a class component
useEffect(() => {})
// ...
}
}
To conclude, your error seems to be appearing as if you're using reducer inside click handler. Check the example Bad1 to resolve your issue. What I mean here is you shouldn't be doing like this:
onClick={()=>productDis({type:'add',payload:'pen'})}
In the onClick handler, dispatch the action and inside a method use that reducer.

Categories

Resources