How do I avoid repeated code when using useState hook in react? - reactjs

I have three places where I'm using the useState hook and I had to write it three times. I want to know if there is a way I could write once instead of having to repeat over again.
const Header = () => {
const [btnOpen, setbtnOpen] = useState(false);
const MobileNavigator = () => {
setbtnOpen(!btnOpen);
}
const [dropDown, setdropDown] = useState(false);
const dropdown = () => {
setdropDown(!dropDown);
}
const [openSearchform, setSearchform] = useState(false);
const formdown = () => {
setSearchform(!openSearchform);
}

you can use HOC(higher order component) for reusing component logic
reference
medium article with example

Related

A variable doesn't update when using the useState() hook

I'm doing a social networking project on React
I wanted to replace one component from class - to functional and use hooks, and a global problem appeared:
When I go to a new user, the page displays the status of the previous one
I use useState() hook, debugged everything, but for some reason when a new status component is rendered, it doesn't update
const ProfileStatus = (props) => {
const [edditMode, setEdditMode] = useState(false);
const [status, setValue] = useState(props.status || "Empty");
const onInputChange = (e) => {
setValue(e.target.value);
};
const activateMode = () => {
setEdditMode(true);
};
const deactivateMode = () => {
setEdditMode(false);
props.updateUserStatus(status);
};
I thought the problem was that the container component was still a class component, but by redoing it, nothing has changed
One way to solve this is by using the useEffect hook to trigger an update when props change. You can use the hook to do comparison between current props and previous props, then update status in the state.
Use this as reference and adapt according to your own code.
const ProfileStatus = (props) => {
const [edditMode, setEdditMode] = useState(false);
const [status, setValue] = useState(props.status || "Empty");
useEffect(() => {
setValue(props.status || "Empty");
}, [props.status]);
const onInputChange = (e) => {
setValue(e.target.value);
};
const activateMode = () => {
setEdditMode(true);
};
const deactivateMode = () => {
setEdditMode(false);
props.updateUserStatus(status);
};

How to write React hooks with called functions?

I have a useQuery() hook:
const useQuery = () => {
const router = useRouter();
const build = (query) => {}
const read = () => {}
}
export default useQuery;
I want to be able to use hook in my code like:
const query = useQuery();
useEffect(()=>{
const oldQuery = query.read();
if(oldQuery.length === 0) query.build(newQuery);
},[])
but every time I try to call query.read() I get error Property 'read' does not exist on type 'void'. It looks like scope issue. How can I fix it?
You need a return statement in useQuery:
const useQuery = () => {
const router = useRouter();
const build = (query) => {}
const read = () => {}
return { router, build, read }
}
You may also want to consider memoizing these functions, so that code which consumes this hook doesn't do unnecessary work because it thinks the functions have changed:
const build = useCallback((query) => {}, []);
const read = useCallback((() => {}, []);

React infinity loop when making HTTP calls using useEffect

I am trying to make 2 HTTTP calls inside a React component that will then call the setters for 2 properties that are defined using useState. I have followed what I thought was the correct way of doing so in order to prevent inifinite rerendering but this is still happening. Here is my code:
function Dashboard({ history = [] }) {
const [teamInfo, setTeamInfo] = useState(null);
const [survey, setSurvey] = useState(null);
const [open, setOpen] = useState(false);
const user = getUser();
const getSurveyHandler = async () => {
const surveyResponse = await getSurveys('standard');
setSurvey(surveyResponse.data);
};
const getTeamInfoHandler = async () => {
const teamInfoResponse = await getTeamInfo(user.teamId);
setTeamInfo(teamInfoResponse);
};
useEffect(() => {
document.body.style.backgroundColor = '#f9fafb';
getSurveyHandler();
getTeamInfoHandler();
}, [survey, teamInfo]);
As you can see, I have defined the functions outside of the useEffect and passed in the two state variables into the dependency array that will be checked to prevent infinite rerendering.
Can anyone see why this is still happening?
Thanks
You are setting survey and teamInfo in your functions with a dependency on them in your useEffect.
useEffect runs everytime a dependency changes. You are setting them, causing a rerender. Since they changed, the useEffect runs again, setting them again. The cycle continues.
You need to remove those.
useEffect(() => {
document.body.style.backgroundColor = '#f9fafb';
getSurveyHandler();
getTeamInfoHandler();
}, []);
The only other thing recommended is to move async functions inside the useEffect unless you need to call them from other places in the component.
useEffect(() => {
const getSurveyHandler = async () => {
const surveyResponse = await getSurveys('standard');
setSurvey(surveyResponse.data);
};
const getTeamInfoHandler = async () => {
const teamInfoResponse = await getTeamInfo(user.teamId);
setTeamInfo(teamInfoResponse);
};
document.body.style.backgroundColor = '#f9fafb';
getSurveyHandler();
getTeamInfoHandler();
}, []);

Why my custom hook causes infinite data refetching?

My component gets the hashed-id from the query string, then calls api with that hash to fetch a post for review.
eslint forces me to add my custom hook to dependency array.
fetchpost();
}, [query]);
But doing this causes an infinite loop. In order to stop it I need to disable this eslint rule, as seen below.
// component file
const history = useHistory();
const dispatch = useDispatch();
const query = useQuery();
const [post, setPost] = useState(null);
const [hash, setHash] = useState(null);
useEffect(() => {
const fetchpost = async () => {
const hash = query.get("hashed_id");
const post = await fetchReviewPost(
`/api/posts/${hash}/review`
);
setHash(hash);
setPost(post);
};
fetchpost();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// utils file
import { useLocation } from "react-router-dom";
export const getCurrentURL = () => {
return document.URL;
};
export const useQuery = () => {
const queryString = useLocation().search;
return new URLSearchParams(queryString);
};
Dan Abramov writes An infinite loop may also happen if you specify a value that always changes in the dependency array.
Is that the case here? Is query reference different on every render? And why eslint wants to put it in a dependency array?
He also says removing a dependency you use (or blindly specifying []) is usually the wrong fix. Which I sort of did by disabling the eslint rule.
Any thoughts?
If you really want to keep sticking to eslint suggestions and using the useQuery hook, here is an alternative way:
// component file
const history = useHistory();
const dispatch = useDispatch();
const q = useQuery();
const [query] = useState(q);
const [post, setPost] = useState(null);
const [hash, setHash] = useState(null);
useEffect(() => {
const fetchpost = async () => {
const hash = query.get("hashed_id");
const post = await fetchReviewPost(
`/api/posts/${hash}/review`
);
setHash(hash);
setPost(post);
};
fetchpost();
}, [query]);
At this point the query value keeps constant across the subsequent function calls.
However, I'd remove the useQuery hook, and place its content straight into the fetchpost function.

How can I use custom method in UseEffect?

How can I use custom method in useEffect??
If I create many components and they use same fetch function, Shoud I declare fetch function in every component's effect?? The function does same work??
As far as I know, If I want to use component's state in useEffect, I should declare and call that function in useEffect likes example 1.
But I want to declare the function other js file. Because it was called other components.
According to Dan Abramov (https://overreacted.io/a-complete-guide-to-useeffect/), If I want to move function, I must use useCallback method.
But I didn't understand well. Please give me any advice this issue.
1. Component.js
const Component = () => {
const [id,setId] = useState(0);
const dispatch = useDispatch();
useEffect(() => {
fetch(`url/${id}`).then(res => dispatch({type: success, payload: res}))
},[id])
}
2. Component.js
const Component = () => {
const [id, setId] = useState(0);
useEffect(()=> {
callApi(id)
},[id])
}
Api.js
const callApi = (id) => {
const dispatch = useDispatch();
return fetch(`url/${id}`).then(res => dispatch({type:success, payload:res})
}
Shoud I declare fetch function in every component's effect?
Extract a custom hook, useFetch(), with the same fetch functionality.
// custom hook
const useFetch = (id) => {
const [data, setData] = useState(null);
useEffect(
() => {
async function fetchData() {
const res = await fetch(`url/${id})
setData(res);
}
fetchData();
}, [id] // id as dependency
)
return data;
}
// sample component using custom hook
const Component = (props) => {
const dispatch = useDispatch();
const data = useFetch(props.id); // use custom hook
useEffect(
() => {
if (data) {
dispatch({type: success, payload: data});
}
}, [data] // dispatch every time data changes
)
}
Since multiple of your components perform the same action within useEffect, you can extract out the code into a custom hook and use it in all the components
useFetch.js
export const useFetch = () => {
const dispatch = useDispatch();
useEffect(() => {
fetch(`url/${id}).then(res => dispatch({type: success, payload: res}))
},[id])
}
Now in the component you can write
const Component = () => {
const [id, setId] = useState(0);
useFetch(id);
}

Resources