Exploring the challenges of conditional rendering in React custom components
P粉301523298
P粉301523298 2023-08-18 15:58:43
0
2
589
<p>I'm having an issue with conditional rendering in a custom component in my React application. I've been working on creating a custom component for conditional rendering that only renders content when certain conditions are met. Here is my current code: </p> <pre class="brush:js;toolbar:false;">import React from 'react'; function ConditionalRenderComponent({ condition, children }) { return ( <div> {condition && children} </div> ); } export default ConditionalRenderComponent; </pre> <p>While using this custom component, I noticed that the content passed through the <code>children</code> attribute even when <code>condition</code> is <code>false</code> Also accessed and executed. This is different from the behavior of using <code>condition && <div></div></code> directly without a custom component, where the condition is <code>false</code> Access to the content is properly blocked. </p> <p><strong>Here's how I use custom components:</strong></p> <pre class="brush:js;toolbar:false;">import React, { useState, useEffect } from 'react'; import ConditionalRenderComponent from './ConditionalRenderComponent'; function App() { const [userData, setUserData] = useState(null); const isLoggedIn = userData !== null; useEffect(() => { setTimeout(() => { setUserData({ username: 'john_doe', email: 'john@example.com' }); }, 1000); }, []); return ( <div> <h1>Welcome to my app</h1> <ConditionalRenderComponent condition={isLoggedIn}> <div> <p>Hello, {userData.username}! </p> <p>Your email: {userData.email}</p> </div> </ConditionalRenderComponent> {!isLoggedIn && <p>Please log in to access user data. </p>} </div> ); } export default App; </pre> <p>In the example above, <code>userData</code> is initially <code>null</code> and is later populated with data retrieved from the server. The <code>isLoggedIn</code> condition depends on whether <code>userData</code> is populated. </p> <p>However, even though the <code>isLoggedIn</code> condition is <code>false</code> (before getting the data), the content inside <code>ConditionalRenderComponent</code> still tries to access and Display <code>userData</code>. This results in the error message: "TypeError: Cannot read properties of null (reading 'username')". </p> <p><strong>Comparison to standard conditional rendering: </strong></p> <p>Here is a comparison of behavior using the standard <code>condition && <div></div></code> approach: </p> <pre class="brush:js;toolbar:false;">// Standard conditional rendering {isLoggedIn && ( <div> <p>Hello, {userData.username}! </p> <p>Your email: {userData.email}</p> </div> )} </pre> <p>As shown in the code above, using standard methods correctly prevents access to the value of <code>userData</code> when the condition is <code>false</code>. </p> <p>I'm not sure why this difference in behavior occurs when using a custom component with the <code>children</code> attribute. I've tried debugging and looking for solutions but I haven't found a clear answer yet. </p> <p> Any insights or suggestions as to why this might be happening and how to modify the custom component to achieve the expected conditional rendering behavior for the <code>children</code> attribute would be greatly appreciated. </p> <p>Thanks in advance for your help! </p>
P粉301523298
P粉301523298

reply all(2)
P粉533898694

Why does this happen?

So I think the reason this is happening is that, in the ConditionalRenderComponent component, the child elements are passed to it as properties (just like arguments to a function). JSX expressions are evaluated as arguments to functions.

This means that even if condition is false, children will still be evaluated before being passed to the ConditionalRenderComponent function.

Easy to understand examples

You give a child a PlayStation (on your left hand) and a math paper score (on your right hand) and say that if his/her score is over 90 /100, he/she will get a PlayStation.

Since the child can already see the PlayStation in your left hand (pass children as a JSX expression), he/she has already started using it before the condition is checked.

Now, if you clench your fist, it's equivalent to passing children as a function, and he/she can't evaluate what's in your left hand before checking whether the condition in your right hand is true.

solution

We modify our custom component by using a function as a child element instead of rendering children directly in the component. This way you ensure that children is only evaluated if condition is true.

Changes to ConditionalRenderComponent

function ConditionalRenderComponent({ condition, children }) {
  return (
    <div>
      {condition && children()}
    </div>
  );
}

Changes to rendering ConditionalRenderComponent

<ConditionalRenderComponent condition={isLoggedIn}>
        {() => (
          <div>
            <p>你好,{userData.username}!</p>
            <p>你的电子邮件:{userData.email}</p>
          </div>
        )}
</ConditionalRenderComponent>
P粉543344381

I don't recommend creating a component for conditional rendering, but if you want to do so, you can use the render properties pattern so that the function is only called when the condition is true. This prevents the expression from being evaluated immediately.

function If({ condition, render }) {
  if (!condition) return null;

  return render();
}

export default function App() {
  const [state, setState] = useState(null);

  useEffect(() => {
    wait(1000).then(() => {
      setState({ hello: "world" });
    });
  }, []);

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>

      <If condition={Boolean(state)} render={() => <p>{state.hello}</p>} />
    </div>
  );
}

Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template