I created a custom hook inside a react function component .Inside the custom hook, I wanted to access some random date (stored inside a state 'data') which is also defined inside the function component so I used useEffect hook and provided the 'data' as a dependency. The code works just fine and I can also console log 'data' but the terminal showing "React Hook useEffect has an unnecessary dependency: 'data'" .
I tried removing 'data' from the dependency array, but then I can't console log 'data'. Anyone please explain me why useEffect showing this behavior inside custom hooks also I need to know if it is a right practice to create a custom hook inside a function component. Thank you
import './App.css';
import { useEffect, useState } from 'react'
function App() {
const [data, setData] = useState()
useEffect(() => {
setData('some random data')
}, [])
const useData = () => {
useEffect(() => {
console.log(data)
}, [data])
}
useData()
return (
<>
</>
)
}
export default App;
Related
Question:
WHY does the code without array always iterate in relation compare to the code with array that only execute once?
Stackblitz:
Without array:
https://stackblitz.com/edit/react-ts-9w3cgd?file=App.tsx
With array:
https://stackblitz.com/edit/react-ts-xrrvan?file=App.tsx
Thank you!
Without array
import axios from 'axios';
import * as React from 'react';
import { useEffect, useState } from 'react';
import './style.css';
export default function App() {
const [data, setData] = useState();
useEffect(() => {
axios
.get('https://jsonplaceholder.typicode.com/users')
.then((result) => setData(result.data));
//console.log(data);
console.log('t');
});
return (
<div>
<h1>Hello StackBlitz!</h1>
<p>Start editing to see some magic happen :)</p>
</div>
);
}
With array
import axios from 'axios';
import * as React from 'react';
import { useEffect, useState } from 'react';
import './style.css';
export default function App() {
const [data, setData] = useState();
useEffect(() => {
axios
.get('https://jsonplaceholder.typicode.com/users')
.then((result) => setData(result.data));
console.log(data);
//console.log("t");
}, []);
return (
<div>
<h1>Hello StackBlitz!</h1>
<p>Start editing to see some magic happen :)</p>
</div>
);
}
From the official React hooks documentation:
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. This isn’t handled as a special case — it follows directly from how the dependencies array always works.
Basically, a useEffect hook with an empty dependency array should run only once when the component mounts.
Alternatively, if we were to not include a dependency array, the callback will run every time the component re-renders.
Which occurs whenever there is a change in state or the state of any of it's parent components.
With that said, it's probably better to avoid using the useEffect hook without a dependency array, since we would usually want to use it to act in a specific way upon the change of a specific state.
Using it in such a way is valid, but not really advised.
I'm writing chat app using react js and socket.io library.
All the logic where I subscribe to events form server and emit some events is written in useEffect of custom hook.
Then I return all data I need from this custom hook and reuse it in components that I need. However, I realized that logic written in useEffect is called every time I import this custom hook to external component.
If I put all the logic outside of useEffect, it's called even more times than custom hook is imported.
How do I prevent it if it's possible at all?
If it's not possible, what solution could you please suggest? I don't want to use redux for this app, I thought to keep everything in this custom hook component and just reuse data from it where I need.
I can't share working example because it won't work without server part so here is a simple codesandbox example. You can see in console that it's rendered twice.
https://codesandbox.io/s/custom-hook-bfc5j?file=/src/useChat.js
It renders twice because you call useChat() two times in your app (one in App.js, other in Text.js) What you can do is to create a reference of useChat component in your App.js and pass is as a prop to Text.js like:
App.js
import React from "react";
import useChat from "./useChat";
import Text from "./Text";
import "./styles.css";
export default function App() {
const myUseChat = useChat();
const { printMessage } = myUseChat;
return (
<div className="App">
<button onClick={printMessage}>Print</button>
<Text myUseChat={myUseChat} />
</div>
);
}
Text.js
import React from "react";
import useChat from "./useChat";
import "./styles.css";
export default function Text(props) {
const { text } = props.myUseChat;
return <div className="App">{text}</div>;
}
If you want to set up some side effects once but also consume the resulting data in multiple places, one way is to use the context feature.
// ws/context.jsx, or similar
const WsContext = React.createContext(defaultValue);
export const WsProvider = props => {
const [value, setValue] = useState(someInitialValue);
useEffect(() => {
// do expensive things, call setValue with new results
});
return (
<WsContext.Provider value={value}>
{props.children}
</WsContext.Provider>
);
};
export const useCustomHook = () => {
const value = useContext(WsContext);
// perhaps do some other things specific to this hook usage
return value;
};
You can expect the hook to work in any component that is a descendant of <WsProvider> in React's rendered tree of elements.
If you use the hook in a non-descendant of the provider component, the value returned will be the defaultValue we initialized the context instance with.
I have a component, example:
function MyComponent() {
const tests = useSelector(state => state.waterfall.tests)
return <div>{value}</div>
}
I want to move use selector to a separate file.
Question - if I move useSelector to file as const
export const testsValue = () => { useSelector(state => state.waterfall.tests) }
and import it in component - does it will work or I will have problems/side effects because Hooks must be defined on top level?
What you are trying to do is in React called a Custom Hook
there are a few recommendations with using custom hooks:
call the custom hook only in a react component or other custom hook
don't call it conditionally
the hook name should start with "use"
otherwise it should work without any problems.
why does my page renders twice, then after fetching data from api, it will re-render again twice. here's my code
import React, {useState, useEffect} from "react"
import axios from "axios"
function Pokemon()
{
const [pokeList,setPokeList] = useState([]);
useEffect(()=>{
console.log('useState-mounted')
axios.get("https://pokeapi.co/api/v2/pokemon?limit=151")
.then((response) => {
setPokeList(response.data.results);
})
},[])
const generationOnePokemon = pokeList.map(i => {
return <h3 key={i.url}>{i.name}</h3>
})
return(
<>
<h1>Shop</h1>
{console.log('page-rendered')}
{generationOnePokemon}
</>
)
}
export default Pokemon
Result from console
This is the expected behaviour when using <React.StrictMode>. It only happens in development mode, so there are no production implications with this.
It ensures that your setState calls are properly implemented and do not rely on only being executed once.
You can read more about React StrictMode here.
I'm developing a small phone book web page with React and when a person is either added or person's info is updated I want a small notification to show on the page. I'm doing this by using React hooks in order to give state to variables message and errorMessage.
After an update, the message in question receives its content (e.g. "New contact added") and setTimeout(() => {setMessage('')}, 6000) is used to clear the message.
I've tried to search every thread I've found with the key words "TypeError, setTimeout" etc. with no results. I don't even understand what is the problem. I'f I remove the setTimeout altogether everything works fine (the message won't disappear).
Code bellow isn't the code I use but it does contain the same exact problem, namely setTimeout causes TypeError. There is no update of contacts, we just try to change the state of the message with setTimeout.
import React, {useState, setTimeout} from 'react';
const Test = () => {
//Setting up message
const [message, setMessage] = useState('')
//Change the state of message to Testing after 6 seconds
setTimeout(() => {setMessage("Testing")}, 6000)
return (
<div></div>
)
}
export default Test
setTimeout comes with a JavaScript and NOT from React,
As you imported setTimeout from react, you are getting this error,
import React, {useState, setTimeout} from 'react';
just remove setTimeout from import like,
import React, {useState} from 'react';
You are importing setTimeout from React, that's the issue. Just remove it from the import, and you will be fine since its a native javascript method.
Also, try to use useEffect when you are making these kinds of changes.
import React, { useState, useEffect } from 'react';
useEffect(() => {
const timeout = setTimeout(() => {setMessage("Testing")}, 6000)
return () => {
clearTimeout(timeout)
}
});
This avoids setting up timeout every time component is rendered and clears it when unmounted.