What's the way to pass a useRef to a custom hook? I'm creating a custom hook to trigger an animation when the user reaches a section with react-intersection-observer and if the user passes more than one the animation doesn't happen again.
If i pass the ref like this:
useAnimation parameters: const useAnimation = (customRef) and the setView method from react-intersection-observer like this: const { ref: customRef, inView: elementIsVisible } = useInView(); i get Parsing error: Identifier 'customRef' has already been declared. If i delete the ref: it happens the same thing.
If i console.log the customRef parameter i get current: undefined
Here is the full code:
useAnimation:
import React, { useEffect, useState, useRef, useContext } from "react";
import { DisableAnimationContext } from "../context/disableAnimationContext";
import { useInView } from "react-intersection-observer";
const useAnimation = (customRef) => {
const { setdisableAnimation } = useContext(DisableAnimationContext);
const { ref: customRef, inView: elementIsVisible } = useInView();
const [animationHappened, setAnimationHappened] = useState(false);
const [animationCounter, setAnimationCounter] = useState({
counter: 0,
});
useEffect(() => {
if (elementIsVisible) {
setdisableAnimation(true);
var currentState = animationCounter.counter;
setAnimationCounter({
...animationCounter,
counter: (currentState += 1),
});
}
if (animationCounter.counter > 0) {
setAnimationHappened(true);
}
}, [elementIsVisible]);
return { elementIsVisible, animationHappened };
};
export default useAnimation;
Here is how i call the custom hook in a component:
import React, { useRef, useContext } from "react";
import useAnimation from "../../../hooks/useAnimation";
const aboutRef = useRef();
console.log(aboutRef);
const { elementIsVisible, animationHappened } = useAnimation(aboutRef);
Related
I'm managing a context for the classes of my school so that i don't have to fetch on every page, for this i write this custom component and wrap my entire app in it.I whenever i fetch the classes i store them in the redux store so that i can use it again and again.
And before fetch i check the store if the classes exist there i don't hit the API instead get the classes from there. in my dependency array i put navigate, dispatch and Classes because react was complaining about that. but this cause infinite loop
here is my code
// ** React Imports
import { createContext, useEffect, useState } from 'react';
import { useDispatch, connect } from 'react-redux';
// ** Config
import { toast } from 'react-hot-toast';
// ** custom imports
import { useNavigate } from 'react-router-dom';
import { CLASSES_API_HANDLER } from 'src/redux/actions/classes/classes';
// ** Defaults
const defaultProvider = {
classes: [],
setClasses: () => null,
loading: true,
setLoading: () => null,
};
export const ClassesContext = createContext(defaultProvider);
const ClassesProvider = ({ children, CLASSES }) => {
const dispatch = useDispatch();
const navigate=useNavigate()
const [classes, setClasses] = useState(defaultProvider.classes);
const [loading, setLoading] = useState(defaultProvider.loading);
useEffect(() => {
const getClasses = async () => {
if (CLASSES.length > 0) {
setClasses(CLASSES);
setLoading(false);
return;
}
try {
const Data = await dispatch(CLASSES_API_HANDLER());
setClasses(Data.result);
setLoading(false);
} catch (ex) {
navigate("/error")
console.log(ex);
}
}
getClasses()
}, [CLASSES,navigate,dispatch]);
const values = {
classes:classes,
setClasses:setClasses,
loading:loading,
setLoading:setLoading,
};
return (
<ClassesContext.Provider value={values}>
{children}
</ClassesContext.Provider>
);
};
function mapStateToProps(state) {
return { CLASSES: state.CLASSES };
}
export default connect(mapStateToProps)(ClassesProvider);
I'm trying to fix the infinite loop
I created the following context.tsx:
import { createContext } from 'react';
export interface LibsAndComponentsInterface {
data: unknown[];
}
export const LibsAndComponentsContext = createContext<
LibsAndComponentsInterface | undefined
>(undefined);
And use it in my Wrapper component:
import { ReactNode, useContext, useEffect, useMemo, useState } from 'react';
import { LibsAndComponentsContext } from 'core/context/dataContext';
import { useApiService } from 'common/hooks/useApiService';
import { useAuth } from 'common/contexts/Authentication';
// Wrap your App component with this
export function LibsAndComponentsProvider({
children,
}: {
children: ReactNode;
}) {
const [libs, setLibs] = useState<unknown[]>([]);
const [components, setComponents] = useState<unknown[]>([]);
const { isAuthenticated } = useAuth();
const { sendRequest } = useApiService();
useEffect(() => {
.....
}, []);
const ctxValue = useMemo(
() => ({
data: [...libs, ...components],
}),
[libs, components],
);
return (
<LibsAndComponentsContext.Provider value={ctxValue}>
{children}
</LibsAndComponentsContext.Provider>
);
}
export function useLibsAndComponents() {
const ctx = useContext(LibsAndComponentsContext);
if (ctx == null) {
throw new Error(
'useLibsAndComponents must be inside LibsAndComponentsProvider',
);
}
return ctx;
}
I get now an error returned for <LibsAndComponentsContext.Provider value={ctxValue}>
'React' refers to a UMD global, but the current file is a module. Consider adding an import instead.
'React' must be in scope when using JSX
I do not see where I am referencing as a module, could somebody point me to the error?
Adding import React from "react"; was the embarrassing solution.
So Im receiving the following error when using a useDispatch inside a useEffect
Line 58:5: React Hook "useDispatch" 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
I have a component like follows
import { useSelector, useDispatch } from 'react-redux';
import { myModules } from '#/redux/modules';
const Component = () => {
const images = useSelector(getImages)
useEffect( () => {
useDispatch(myModules.actions.setMaskVisible(true));
}, [images]);
}
setMaskVisible is pretty much
export const SET_MASK_VISIBLE = 'caps/SET_MASK_VISIBLE';
const setMaskVisible = payload => {
return {
type: SET_MASK_VISIBLE,
payload
};
};
export default { setMaskVisible }
Im basically trying to change a redux value, when images update. Why desnt it work?
Have a look at the documentation. You call useDispatch to get a function and call that function:
import { useSelector, useDispatch } from 'react-redux';
import { myModules } from '#/redux/modules';
const Component = () => {
const images = useSelector(getImages)
const dispatch = useDispatch();
useEffect( () => {
dispatch(myModules.actions.setMaskVisible(true));
}, [images]);
}
import React from "react"
import { useSelector } from 'react-redux'
import { useLocation } from "react-router"
const BreadCrumb = () => {
const location = useLocation()
const currentPageTitle = useSelector(state => {
// match current location to get the currrent page title from redux store and return it
})
return (
<h2>Home / { currentPageTitle}</h2>
)
}
export default BreadCrumb
This code works fine in the initial render and I do get the intended result { currentPageTitle } but the UI doesn't seem to re-render and stays the same despite route change. Although, if I console.log( location ) before the return statement, it logs successfully on route change. What is the issue here?
I would suggest that you use useEffect hook:
import React, { useEffect, useState } from "react"
import { useSelector } from 'react-redux'
import { useLocation } from "react-router"
const BreadCrumb = () => {
const location = useLocation()
const [currentLocation, setCurrentLocation] = useState('')
const currentPageTitle = useSelector(state => {
console.log(currentLocation);
// Do something with currentLocation
})
useEffect(() => {
if (location) {
setCurrentLocation(location.pathname)
} else {
setCurrentLocation('')
}
}, [location])
return (
<h2>Home / { currentPageTitle}</h2>
)
}
export default BreadCrumb
I created a custom hook that uses useEffect to update a redux store when certain values in the store change:
import { useRef, useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { index } from '../actions'
import { createSelector } from 'reselect'
import { omit } from 'lodash'
const selectPerformanceSearch = createSelector(
state => omit(state.eventSearch, ['results', 'isFetching']),
items => items
)
export default function useEventSearch() {
const state = useSelector(selectEventSearch)
const dispatch = useDispatch()
const initialFetch = useRef(true)
const { tags, near, filters } = state
const { lng, lat, location } = near
useEffect(() => {
if (!initialFetch.current) {
const data = { tags, lng, lat, filters }
dispatch(index('/event/search', 'EVENT_SEARCH', data))
}
}, [tags, lng, lat, filters, dispatch])
useEffect(() => {
if (initialFetch.current && location) {
initialFetch.current = false
const data = { lng, lat }
dispatch(index('/event/closest', 'EVENT_SEARCH', data))
}
}, [lng, lat, location, dispatch])
}
Then I have a React component that uses the hook like so:
import useEventSearch from '../../hooks/usePerformanceSearch'
const SearchBar = props => {
// ...code
usePerformanceSearch()
/// ...other code
return ( ... )
}
It all works as I would expect but is it ok to just call and use a hook the way I did?