Reputation: 623
This is my first time to develop a react application. I intend to make an Api call when user clicked a button. Is it possible to directly call a hook from an event? If yes, how can I call it if the hook was from a different component?
This is the button (different component)
const paginationButtons = (
<React.Fragment>
<Button onClick={() => viewDetails()}>View Details</Button>
</React.Fragment>
);
function viewDetails() {
//Is it Possible to call fetch data.js and pass the url from here?
}
This is the hook for fetch data.js
import axios from 'axios';
import PropTypes from 'prop-types';
import { useState, useEffect} from 'react';
const propTypes = {
initialUrl: PropTypes.string.isRequired,
initialParams: PropTypes.object
}
export const useFetchData = props => {
PropTypes.checkPropTypes(propTypes, props, 'property', 'useFetchData');
const { initialUrl, initialParams } = props;
const [request, setRequest] = useState({
url: initialUrl,
params: initialParams
});
const [loading, setIsLoading] = useState(false);
const [error, setError] = useState();
const [data, setData] = useState([]);
useEffect(() => {
const {url, params} = request;
const fetchData = async () => {
setIsLoading(true);
try {
const result = await axios(url, {params: {...params}});
setIsLoading(false);
setData(result.data);
} catch(err) {
setIsLoading(false);
setError(err);
}
}
fetchData();
}, [request]);
const doFetch = (params) => {
setRequest({url: request.url, params: params});
};
return {loading, error, data, doFetch};
}
Thank you very much for your help.
Upvotes: 10
Views: 11637
Reputation: 2351
you may need context
const Context = React.createContext(null);
function FetchProvider(props){
const state = useFetchData(props);
return (
<Context.Provider value={state}>{props.children}</Context.Provider>
)
}
function Button(){
const { doFetch } = React.useContext(Context);
const handleClick = React.useCallback(()=> doFetch({id: ""}), [])
return <button onClick={handleClick}>Fetch</button>
}
function ViewDetails(){
const {data, error, loading} = React.useContext(Context);
if (loading) { return <div>Loading</div> }
if (error) { return <div>{error.message}</div> }
return <div>{JSON.stringify(data)}</div>
}
function Page(){
return (
<FetchProvider initialUrl="/url" initialParams={{id: ""}}>
<ViewDetails/>
<Button/>
</FetchProvider >
);
}
Upvotes: 0
Reputation: 2874
What you intend to do is possible, but in my opinion, it shouldn't be done in a real-world application. Here's why.
First, let's consider why we would want to use a hook. React function components had no way of preserving local state and handling lifecycle events between rerenders. This is why hooks were introduced.
Before I use a hook, I would ask myself, "Do I need to handle component state or the component life-cycle?". If not, using a hook is either overkill or an anti-pattern.
In your example, let's consider what you may be trying to do with the viewDetails
function. Obviously, you want to do the fetch call, but what do you intend to do afterward? Let's see some possibilities...
setState
function, or fire an event to be captured by another component. In either case, there's no use of using a hook for fetching data.Thus instead of trying to call a hook like useFetchData
, you should call a function like doFetchData
.
I am not suggesting that you should get rid of the useFetchData
hook. It's just, they serve different purposes. It makes sense to use the hook for any fetch operations that should happen at load time rather than as a response to a user action.
Now that I have given my opinion, here's one way you can trigger a hook based on user action.
// Here we are using a shouldFetch flag to conditionally call a function
function useFetch(url, options, shouldFetch, setShouldFetch) {
const [state, setState] = useState({});
useEffect(() => {
if (shouldFetch) {
doFetch(url, options)
// set the fetch result to local state
.then(data => {
setState(data)
setShouldFetch(false) // avoid infinite calls
})
}
});
return [state]; // You can maybe add fetch status as well
}
Now you can use it like this,
function MyComponent() {
const [shouldFetch, setShouldFetch] = useState(false);
const [fetchData] = useFetch('url', {}, shouldFetch, setShouldFetch);
return (
<Button onClick={() => {setShouldFetch(true)}}>Click Me</Button>
)
}
But really, you shouldn't use a hook this way. A regular doFetchData
call is the way to go. Call some setState functions to preserve the fetched data if you need to.
Upvotes: 15