I was creating a function for my search bar but I receive this error:
Below is the function:
const HandleSearch = async (val) => {
useEffect(() => {
const fetchData = async () => {
const data = await db.collection('accounts').orderBy('date').get();
setAccounts(data.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
};
fetchData();
}, []);
useEffect(() => {
setAccounts(
accounts.filter(
(account) =>
account.name.toLowerCase().includes(search.toLowerCase())
)
);
}, [search, accounts]);
}
you might be using the hooks in a wrong way. as per rules of hooks, hooks can only be called from React function components and from a custom Hooks. Don’t call Hooks from regular JavaScript functions, inside loops, conditions, or nested functions.
As the error message tells hooks can only be called inside function components.
Try removing the async keyword preceding your component. This way React will understand it is a functional component and everything should work fine :)
At the moment it is not actually a component as components are supposed to return JSX. By preceding it with the async keyword it will implicitly return a Promise.
You can read more about async functions here. Promises are a pretty tricky concept and take time to fully understand.
Related
I'm trying been more simlicity but i still learn the power of custom hook.
i'm trully not understand whats wrong with my hook.
const UseMessage = (msg: string, delay: number, cb?: () => void): [setCB: () => void] => {
const [message, setMessage] = useState("");
const setCB = () => {
setMessage(() => msg);
setTimeout(() => {
setMessage(() => msg);
if (typeof cb === "function") {
cb();
}
}, delay);
};
return [setCB];
};
export default UseMessage;
i'm getting error :Error: Invalid hook call. Hooks can only be called inside of the body of a function component.
Custom hook names should always start with lower case use (see beta docs), so change to useMessage.
There are two things which can resolve your problem, here:
First, name your hook with lowercase use and not uppercase. The reason for that is it is wrong according to the React docs.
Here, your UseMessage is not a custom hook for React, just a javascript function. Inside it, you are using useState which is a hook and React won't allow that. The reason is the next point.
Second, according to the rules of React you can utilise any hook only inside the body of a function component or inside the body of another hook function.
I have a functional component in React:
export default function (id) {
const [isReady] = useConfig(); //Custom hook that while make sures everything is ok
useEffect( () => {
if(isReady)
renderBackend(id);
});
async function renderBackend(id) {
const resp = await getBackendData(id);
...
...
}
}
Now, I am passing some props to the Functional Component like this:
export default function (id, props) {
const [isReady] = useConfig(); //Custom hook that while make sures everything is ok
useEffect( () => {
if(isReady)
renderBackend(id);
});
async function renderBackend(id) {
const resp = await getBackendData(id, props); // Passing props to backend
...
...
}
}
Here the props are dynamic based on user input and changes time on time. But my code here is only rendering for the first prop, not on subsequent props. I want to call the backend every time props get updated or being passed. I think we might use useEffect for this, but not totally sure. And I cannot replicate this is codeSandbox as the real code is very complex and have trimmed down to mere basics.
Change
useEffect( () => {
if(isReady)
renderBackend(id);
});
to
useEffect( () => {
if(isReady)
renderBackend(id);
}, [id]);
so useEffect function runs every time id changes
As you do not put useEffect dependency, it will be executed for every re-render (state changed, ...)
So to execute the code within the useEffect every props change, put it to the useEffect dependencies list.
useEffect( () => {
if(isReady)
renderBackend(id);
}, [id, props]);
best practice is destructure your props and put only the affected value in the dependencies.
When using React.useEffect() hook consider that you have to pass the dependencies to gain all you need from a useEffect. The code snippet is going to help you to understand this better.
useEffect(() => {
console.log('something happened here');
}, [one, two, three]);
every time that one of the items passed to [one, two, three] you can see something happened here in your browser developer tools console.
I also should mention that it is not good idea at all to pass complex nested objects or arrays as a dependency to the hook mentioned.
I'm trying to make a function that will reset multiple states and then make an API call, however I'm having trouble making the API call happen AFTER the three states have been set. My function looks like this:
const resetFilters = () => {
setYearFilter("");
setProgressFilter("");
setSearchFilter("");
callAPI();
};
I've tried using Promise.resolve().then(), and tried using async await, but it seems the useState setter function doesn't return a promise. Is there a way to make this all happen synchronously?
You could use useEffect to listen on changes and do each task sequentially
const resetFilters = () => {
setYearFilter("")
}
useEffect(() => {
setProgressFilter("")
}, [yearFilter])
useEffect(() => {
setSearchFilter("")
}, [progressFilter])
useEffect(() => {
callAPI()
}, [searchFilter])
I'm trying to call useState inside an async function like:
const [searchParams, setSearchParams] = useState({});
const fetchData = () => useCallback(
() => {
if (!isEmpty(searchParams)) {
setIsLoading(true); // this is a state hook
fetchData(searchParams)
.then((ids) => {
setIds(ids); // Setting the id state here
}).catch(() => setIsLoading(false));
}
},
[],
);
There are two states I am trying to set inside this fetchData function (setIsLoading and setIds), but whenever this function is executed am getting the error:
Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
What is this Rule of hooks I am breaking here?
Is there any way around to set these states from the function?
PS: I only used the useCallback hook here for calling this function with lodash/debounce
Edit: The function is called inside useEffect like:
const debouncedSearch = debounce(fetchSearchData, 1000); // Is this the right way to use debounce? I think this is created every render.
const handleFilter = (filterParams) => {
setSearchParams(filterParams);
};
useEffect(() => {
console.log('effect', searchParams); // {name: 'asd'}
debouncedSearch(searchParams); // Tried without passing arguments here as it is available in state.
// But new searchParams are not showing in the `fetchData`. so had to pass from here.
}, [searchParams]);
The hook rule you are breaking concerns useCallback because you are returning it as the result of your fetchData;
useCallback should be called on top level; not in a callback, like this:
const fetchData = useCallback(
() => {
if (!isEmpty(searchParams)) {
setIsLoading(true); // this is a state hook
fetchData(searchParams)
.then((ids) => {
setIds(ids); // Setting the id state here
}).catch(() => setIsLoading(false));
}
},
[],
);
The code you wrote is equivalent to
const fetchData = () => { return React.useCallback(...
or even
function fetchData() { return React.useCallback(...
To read more about why you can't do this, I highly recommend this blog post.
edit:
To use the debounced searchParams, you don't need to debounce the function that does the call, but rather debounce the searched value. (and you don't actually the fetchData function that calls React.useCallback at all, just use it directly in your useEffect)
I recommend using this useDebounce hook to debounce your search query
const [searchParams, setSearchParams] = React.useState('');
const debouncedSearchParams = useDebounce(searchParams, 300);// let's say you debounce using a delay of 300ms
React.useEffect(() => {
if (!isEmpty(debouncedSearchQuery)) {
setIsLoading(true); // this is a state hook
fetchData(debouncedSearchParams)
.then((ids) => {
setIds(ids); // Setting the id state here
}).catch(() => setIsLoading(false));
}
}, [debouncedSearchParams]); // only call this effect again if the debounced value changes
I'm trying to use useEffect to fetch data when a component is mounted as follows:
useEffect(() => {
axios.get(myUrl)
.then(response => {
setName(response.name);
setAge(response.age);
}).catch(error => {
console.log(error);
});
}, [myUrl, setName, setAge]);
setName and setAge are coming from a context as follows:
import MyContext from './context';
const MyComponent = props => {
const {
setName,
setAge
} = useContext(MyContext);
This issue is that functions, arrays, and objects all register as "changed" dependencies in a useEffect call so it ends up in an infinite loop where it's fetching that data over and over and over again. Can I memoize a function from a context so it knows to only call that effect once?
Note: The consumer of my context is several levels above this component so I'm guessing there's nothing I can do there.
I think you need to define custom context tied to something like userId.
This will provide you a stable identifier which will change only when it is necessary.
There is no way to memoize a function from a context.
That said, you can use a custom hook for useMount, either by importing it from the react-use library or by simply looking at the source code there and declaring it yourself. The following shows how to define useMount and useEffectOnce and then utilize useMount as a workaround:
const useEffectOnce = (effect) => {
useEffect(effect, []);
};
const useMount = (fn) => {
useEffectOnce(() => {
fn();
});
};
useMount(() => {
axios.get(myUrl)
.then(response => {
setName(response.name);
setAge(response.age);
}).catch(error => {
console.log(error);
});
});
With this approach, you don't need to change anything about your context declaration, context provider, or context consumer.