skip to Main Content

So I have this wrapper component

const AccessControl = ({ content, children }: acProps) => {
  return content ? children : null;
};

And I have used it like this

<AccessControl content={false}>
  {console.log("Ashish")}
  <div>This is executing</div>
</AccessControl>

So it is executing the children even the content is false. If this is how it works, how can I use a wrapper component to avoid ternary operators?

4

Answers


  1. JS evaluates its arguments before calling a function, including JSX expressions before they are passed as children to a component – which is why with content={false} the <div>This is executing</div> is not showing up, yet you can still see the console.log.

    To get around this you can pass children as a function like this:

    <AccessControl content={false}>
      {() => (
        <>
          {console.log("Ashish")}
          <div>This is executing</div>
        </>
      )}
    </AccessControl>
    

    and call children in AcessControl component (remember to change type in acProps from ReactNode to () => ReactNode)

    const AccessControl = ({ content, children }: acProps) => {
      return content ? children() : null;
    };
    
    Login or Signup to reply.
  2. Your wrapper component does do its job, the children won’t be rendered to the DOM, it’s just that functions inside a component will be run when it is rendered, which is why the console.log is running.

    But a better way to go about this is to just conditionally render it via short circuiting.

    {content && (<div>This is executing</div>)}

    Login or Signup to reply.
  3. You can refactor the component into a wrapper component that takes multiple children and renders them based on their type. This approach is based on the concept of compound components, which are a pattern for creating more flexible and reusable components.

    Example:

    const AccessControl = ({ children }) => {
     const renderChildren = useCallback(() => {
       return React.Children.map(children, (child) => {
         if (child?.type === AccessControl.Content && child.props.condition) {
           return child.props.children;
         }
         return null;
       });
     }, [children]);
    
     return <>{renderChildren()}</>;
    };
    
    AccessControl.Content = ({ children, condition }) => children;
    

    In the above code, The AccessControl is a wrapper component that takes multiple children. It uses the React.Children.map function to iterate over its children and render them based on their type and a condition prop.

    The AccessControl.Content component is a special child component that takes a condition prop and its own children. If the condition prop is truthy, it renders its children; otherwise, it renders null.

    After that, you can use this refactored AccessControl component like this:

    <AccessControl>
     <AccessControl.Content condition={content}>
       {children}
     </AccessControl.Content>
    </AccessControl>
    

    This will allows you to avoid using a ternary operator and makes your code more flexible and maintainable. It also provides a clear and explicit way to control the rendering of different parts of your component.

    This is a suggestion, so if not usefull, please tell me so I will remove this answer immediately. 🙂

    Login or Signup to reply.
  4. Because it’s an inline code and always runs despite of condition it true or false.

    In React typically we use inline code only for conditional rendering. You can’t use any business logic there if you want try this.

    const AccessControl = ({ content, children }) => {
      if(content){
        console.log("I am rendering");
        //logic
        return children;
      }
    };
    

    Or even better without any logic just for rendering like this

    {content && (<div>This is executing</div>)}
    

    Using Children is uncommon and can lead to fragile code More on

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search