Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extend Functional Components in ReactJS

I have two functions here. It's evident that there's a lot of overlap between them, and I'd like to create a parent function that both functions below will extend from.

function Home() {
  const [state, dispatch] = useContext(NTTrackerContext);
  const history = useHistory();

  function pushhistory(url, callback) {
    history.push(url);
    callback();
  }

  function teamhome2_message() {
    const info = message.error({
      content: "Log in to access team data!",
    });
  };

  function checklogin(callback) {
    if (!state.user.authenticated) 
      pushhistory("/accounts/login", function(){teamhome2_message();});
    else 
      callback();
  }
  # ...
function APIHome() {
  const [state, dispatch] = useContext(NTTrackerContext);
  const history = useHistory();

  function pushhistory(url, callback) {
    history.push(url);
    callback();
  }

  function apihome2_message() {
    const info = message.error({
      key: "apihome2",
      content: "Log in to access team API!",
    });
  };

  function checklogin(callback) {
    if (!state.user.authenticated) 
      pushhistory("/accounts/login", function(){apihome2_message();}); 
    else 
      callback();
  }
  # ...

However, I've been dealing with invalid state/hook calls, not creating the instances correctly. I ended up having no idea how to continue.

Can anybody please provide examples of how I should extend the two home functions from a parent function?

These I have looked at:

  • Thread one: didn't help as I'm not using redux
  • Thread two: this is only regarding the usage in return statements; I'm looking for usage outside the return statement in a function-based component.

Thanks in advance.

like image 895
crimsonpython24 Avatar asked Mar 30 '26 23:03

crimsonpython24


1 Answers

Here are three different ways to approach the problem. I'd probably go for the second method myself.

1. Share functions

A simple approach is to move your helpful functions outside of the function components, so that they can be used by multiple components. These could be in a utilities file that you import from Home.jsx and APIHome.jsx, but here I've coded them as if they're all in one file for simplicity. Because they're outside the function components, you need to pass in whatever data you need from within each component; here, history.

function pushhistory(history, url, callback) {
  // added `history` argument
  history.push(url);
  callback();
}

function checklogin(state, history, loginMessage, callback) {
  // added `state`, `history`, `message` arguments
  if (!state.user.authenticated) 
    pushhistory(history, "/accounts/login", loginMessage);
  else 
    callback();
}

function Home() {
  const [state, dispatch] = useContext(NTTrackerContext);
  const history = useHistory();

  function teamhome2_message() {
    const info = message.error({
      content: "Log in to access team data!",
    });
  };

  # ...

  checklogin(state, history, teamhome2_message, callback);
}

function APIHome() {
  const [state, dispatch] = useContext(NTTrackerContext);
  const history = useHistory();

  function apihome2_message() {
    const info = message.error({
      key: "apihome2",
      content: "Log in to access team API!",
    });
  };

  # ...

  checklogin(state, history, apihome2_message, callback);
}

2. Write your own hooks

If you turn these helpers into hooks, you can push the useHistory inside them, which simplifies the code (avoiding passing the history object). Something like this:

function useLogin(state, loginMessage) {
  const history = useHistory();
  if (!state.user.authenticated) {
    history.push(url);
    loginMessage();
    return false;
  }
  return true;
}

function Home() {
  const [state, dispatch] = useContext(NTTrackerContext);
  const history = useHistory();

  function teamhome2_message() {
    const info = message.error({
      content: "Log in to access team data!",
    });
  };

  const loggedIn = useLogin(state, teamhome2_message);
  if (!loggedIn) callback();

  # ...
}

function APIHome() {
  const [state, dispatch] = useContext(NTTrackerContext);
  const history = useHistory();

  function apihome2_message() {
    const info = message.error({
      key: "apihome2",
      content: "Log in to access team API!",
    });
  };

  const loggedIn = useLogin(state, apihome2_message);
  if (!loggedIn) callback();

  # ...
}

3. Higher-order components

As suggested in the comments, you could write a function that generates components, and give it different arguments. Here's an example of that:

function homeComponent(loginMessage, ...) {
  const [state, dispatch] = useContext(NTTrackerContext);
  const history = useHistory();

  function pushhistory(url, callback) {
    history.push(url);
    callback();
  }

  function login_message() {
    const info = message.error(loginMessage);
  };

  function checklogin(callback) {
    if (!state.user.authenticated) 
      pushhistory("/accounts/login", login_message);
    else 
      callback();
  }

  # ...
}

const Home = homeComponent({
  content: "Log in to access team data!",
}, ...);

const APIHome = homeComponent({
  key: "apihome2",
  content: "Log in to access team API!",
});

But in this case you'll need more arguments to decide what to do in the ... of each component, and you'll probably need to pass state into them, which ends up looking like the first type of solution.

like image 199
edemaine Avatar answered Apr 02 '26 12:04

edemaine



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!