Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can you early return with React hooks?

The React docs make it clear that calling hooks conditionally will not work. From the original React hooks presentation, the reason is because React uses the order you call hooks to inject the correct value.

I understand this, but now my question is whether or not it's okay to early return from within a function component with hooks.

So is something like this allowed?:

import React from 'react'; import { useRouteMatch, Redirect } from 'react-router'; import { useSelector } from 'react-redux';  export default function Component() {   const { match } = useRouteMatch({ path: '/:some/:thing' });   if (!match) return <Redirect to="/" />;    const { some, thing } = match.params;   const state = useSelector(stateSelector(some, thing));    return <Blah {...state} />; } 

Technically, the useSelector hook is being called conditionally, however the order when they are called doesn't change between renders (even though it's possible that one less hook will be called).

If this isn't allowed can you explain why it isn't allowed and provide general alternative approaches to early returning in a function component with hooks?

like image 485
Rico Kahler Avatar asked Mar 01 '19 05:03

Rico Kahler


People also ask

Should React Hooks return something?

React hooks are not required to return anything. The React documentation states that: We don't have to return a named function from the effect.

Can React Hooks return component?

As seen in the code examples, using hooks to encapsulate shared logic is a good start, but we can keep exploring more. Inspired by the concept of “partial application”, we can try to return a component with props that have been partially bound from a custom hook.

When should you not use a hook on a React?

1. Changing the Hooks Invocation Order. Hooks should not be called within loops, conditions, or nested functions since conditionally executed Hooks can cause unexpected bugs. Avoiding such situations ensures that Hooks are called in the correct order each time the component renders.

Can a React hook return another hook?

You can't use a hook inside another hook because it breaks the rule Call Hooks from React function components and the function you pass to useEffect is a regular javascript function.


1 Answers

React does not allow you to do an early return prior to other hooks. If a component executes fewer hooks than a previous render, you will get the following error:

Invariant Violation: Rendered fewer hooks than expected. This may be caused by an accidental early return statement.

React can't tell the difference between an early return before a hook call and a hook call that is conditional for some other reason. For instance, if you have 3 calls to useState and you sometimes return after the second one, React can't tell whether you returned after the second useState call or if you put a condition around the first or second useState call, so it can't reliably know whether or not it is returning the correct state for the two useState calls that did occur.

Here's an example that you can use to see this error in action (click the "Increment State 1" button twice to get the error):

import React from "react"; import ReactDOM from "react-dom";  function App() {   const [state1, setState1] = React.useState(1);   if (state1 === 3) {     return <div>State 1 is 3</div>;   }   const [state2, setState2] = React.useState(2);   return (     <div className="App">       <div>State 1: {state1}</div>       <div>State 2: {state2}</div>       <button onClick={() => setState1(state1 + 1)}>Increment State 1</button>       <button onClick={() => setState2(state2 + 1)}>Increment State 2</button>     </div>   ); }  const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement); 

Edit Early return with hooks

The alternative approach I would recommend is to separate the portion after the early return into its own component. Anything needed by the portion after the early return gets passed to the new component as props.

In the case of my example, it could look like the following:

import React from "react"; import ReactDOM from "react-dom";  const AfterEarlyReturn = ({ state1, setState1 }) => {   const [state2, setState2] = React.useState(2);   return (     <div className="App">       <div>State 1: {state1}</div>       <div>State 2: {state2}</div>       <button onClick={() => setState1(state1 + 1)}>Increment State 1</button>       <button onClick={() => setState2(state2 + 1)}>Increment State 2</button>     </div>   ); }; function App() {   const [state1, setState1] = React.useState(1);   if (state1 === 3) {     return <div>State 1 is 3</div>;   }   return <AfterEarlyReturn state1={state1} setState1={setState1} />; }  const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement); 

Edit Early return with hooks

like image 98
Ryan Cogswell Avatar answered Oct 12 '22 23:10

Ryan Cogswell