最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Way to call hooks from button events in react - Stack Overflow

programmeradmin0浏览0评论

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.

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.

Share Improve this question asked May 27, 2019 at 2:36 DanDan 6234 gold badges10 silver badges23 bronze badges 1
  • You can do that if viewDetails is inside a component.import hook,move viewDetails inside component. – Pavindu Commented May 27, 2019 at 4:17
Add a comment  | 

2 Answers 2

Reset to default 15

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...

  1. Update the dom imperatively: You shouldn't do imperative operations unless there is a very specific reason. Let's assume, you want to show an alert, then, of course, you can call an imperative API (like notie.js or sweet-alert for instance).
  2. Update the dom reactively: This is the most common scenario I think. In such a case, you should either call some 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.

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 >
   );
}
发布评论

评论列表(0)

  1. 暂无评论