• React Conditional rendering methods
  • Esteban Herrera
  • The Nuggets translation Project
  • Permanent link to this article: github.com/xitu/gold-m…
  • Translator: Dong Han
  • Proofreader: Jessica Shao, Doctype233

JSX is a powerful extension to JavaScript that allows us to define UI components. But it does not directly support loops and conditional expressions (although adding conditional expressions has been discussed).

If you want to iterate through a list to render multiple components or implement some conditional logic, you have to use pure Javascript, and you don’t have many options for handling loops. More often than not, Map will meet your needs.

But what about conditional expressions?

That’s another story.

There are several options available to you

React has several ways to use conditional statements. And, as with most things in programming, depending on the actual problem you’re trying to solve, some approaches are more appropriate.

This tutorial introduces the most popular conditional rendering methods:

  • If/Else
  • Avoid rendering empty elements
  • The element variables
  • Ternary operator
  • With the operation (&)
  • Call functions now (IIFE)
  • Child components
  • High order Components (HOCs)

As an example of how all of these methods work, we’ll implement a component with view/edit capability:

You can try and copy all examples in JSFiddle.

Let’s start with the primitive implementation of if/else and build it here.

If/else

Let’s build a component using the following states:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {text: ' ', inputText: ' ', mode:'view'}; }}Copy the code

You will use one property to hold the text and another to store the text being edited. The third property will indicate whether you are in Edit or View mode.

Next, add some methods to handle input text, save, and input events:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {text: ' ', inputText: ' ', mode:'view'};
    
    this.handleChange = this.handleChange.bind(this);
    this.handleSave = this.handleSave.bind(this);
    this.handleEdit = this.handleEdit.bind(this);
  }
  
  handleChange(e) {
    this.setState({ inputText: e.target.value });
  }
  
  handleSave() {
    this.setState({text: this.state.inputText, mode: 'view'});
  }

  handleEdit() {
    this.setState({mode: 'edit'}); }}Copy the code

Now, for the render method, in addition to the saved text, check the mode state property to display the edit button or text input box and the save button:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {text: ' ', inputText: ' ', mode:'view'};
    
    this.handleChange = this.handleChange.bind(this);
    this.handleSave = this.handleSave.bind(this);
    this.handleEdit = this.handleEdit.bind(this);
  }
  
  handleChange(e) {
    this.setState({ inputText: e.target.value });
  }
  
  handleSave() {
    this.setState({text: this.state.inputText, mode: 'view'});
  }

  handleEdit() {
    this.setState({mode: 'edit'}); }}Copy the code

Here is the complete code, which you can try to implement in Fiddle:

Babel + JSX:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {text: ' ', inputText: ' ', mode:'view'};
    
    this.handleChange = this.handleChange.bind(this);
    this.handleSave = this.handleSave.bind(this);
    this.handleEdit = this.handleEdit.bind(this);
  }
  
  handleChange(e) {
    this.setState({ inputText: e.target.value });
  }
  
  handleSave() {
    this.setState({text: this.state.inputText, mode: 'view'});
  }

  handleEdit() {
    this.setState({mode: 'edit'});
  }
  
  render () {
    if(this.state.mode === 'view') {
      return (
        <div>
          <p>Text: {this.state.text}</p>
          <button onClick={this.handleEdit}>
            Edit
          </button>
        </div>
      );
    } else {
      return (
        <div>
          <p>Text: {this.state.text}</p>
            <input
              onChange={this.handleChange}
              value={this.state.inputText}
            />
          <button onClick={this.handleSave}>
            Save
          </button>
        </div>
      );
    }
  }
}

ReactDOM.render(
    <App />,
  document.getElementById('root'));Copy the code

If /else is the easiest way to do this, but I’m sure you know that’s not a good way to do it.

It works for simple use cases, and every programmer knows how it works. But there’s a lot of repetition, and the Render method doesn’t look neat.

So let’s simplify it by extracting all conditional logic into two render methods, one to render the text box and the other to render the button:

Class App extends React.Component {//...renderInputField() {
    if(this.state.mode === 'view') {
      return <div></div>;
    } else {
      return( <p> <input onChange={this.handleChange} value={this.state.inputText} /> </p> ); }}renderButton() {
    if(this.state.mode === 'view') {
      return (
          <button onClick={this.handleEdit}>
            Edit
          </button>
      );
    } else {
      return( <button onClick={this.handleSave}> Save </button> ); }}render () {
    return( <div> <p>Text: {this.state.text}</p> {this.renderInputField()} {this.renderButton()} </div> ); }}Copy the code

Here is the complete code, which you can try to implement in Fiddle:

Babel + JSX:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {text: ' ', inputText: ' ', mode:'view'};
    
    this.handleChange = this.handleChange.bind(this);
    this.handleSave = this.handleSave.bind(this);
    this.handleEdit = this.handleEdit.bind(this);
  }
  
  handleChange(e) {
    this.setState({ inputText: e.target.value });
  }
  
  handleSave() {
    this.setState({text: this.state.inputText, mode: 'view'});
  }

  handleEdit() {
    this.setState({mode: 'edit'});
  }
  
  renderInputField() {
    if(this.state.mode === 'view') {
      return <div></div>;
    } else {
      return( <p> <input onChange={this.handleChange} value={this.state.inputText} /> </p> ); }}renderButton() {
    if(this.state.mode === 'view') {
      return (
          <button onClick={this.handleEdit}>
            Edit
          </button>
      );
    } else {
      return( <button onClick={this.handleSave}> Save </button> ); }}render () {
    return (
      <div>
        <p>Text: {this.state.text}</p>
        {this.renderInputField()}
        {this.renderButton()}
      </div>
    );
  }
}

ReactDOM.render(
  <App />,
  document.getElementById('root'));Copy the code

Note that the method renderInputField returns an empty div element when the component is in preview mode.

But this is not necessary.

Avoid rendering empty elements

If you want to hide a component, you can make its render method return NULL, since there is no need to render an empty (and different) element to hold space.

It is important to note that when null is returned, the lifecycle method is fired even though the component is not visible.

For example, the following code implements a counter between two components:

Babel + JSX:

class Number extends React.Component {
  constructor(props) {
    super(props);
  }
  
  componentDidUpdate() {
    console.log('componentDidUpdate');
  }
  
  render() {
    if(this.props.number % 2 == 0) {
        return (
            <div>
                <h1>{this.props.number}</h1>
            </div>
        );
    } else {
      return null;
    }
  }
}

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 }
  }
  
  onClick(e) {
    this.setState(prevState => ({
      count: prevState.count + 1
    }));
  }

  render() {
    return (
      <div>
        <Number number={this.state.count} />
        <button onClick={this.onClick.bind(this)}>Count</button>
      </div>
    )
  }
}

ReactDOM.render(
  <App />,
  document.getElementById('root'));Copy the code

The Number component only renders the value passed by the parent if the parent passes an even Number; otherwise, it returns NULL. Then, when you look at the console output, you’ll see that componentDidUpdate is always called no matter what Render returns.

Going back to our example, change the renderInputField method like this:

  renderInputField() {
    if(this.state.mode === 'view') {
      return null;
    } else {
      return( <p> <input onChange={this.handleChange} value={this.state.inputText} /> </p> ); }}Copy the code

Here is the complete code:

Babel + JSX:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {text: ' ', inputText: ' ', mode:'view'};
    
    this.handleChange = this.handleChange.bind(this);
    this.handleSave = this.handleSave.bind(this);
    this.handleEdit = this.handleEdit.bind(this);
  }
  
  handleChange(e) {
    this.setState({ inputText: e.target.value });
  }
  
  handleSave() {
    this.setState({text: this.state.inputText, mode: 'view'});
  }

  handleEdit() {
    this.setState({mode: 'edit'});
  }
  
  renderInputField() {
    if(this.state.mode === 'view') {
      return null;
    } else {
      return( <p> <input onChange={this.handleChange} value={this.state.inputText} /> </p> ); }}renderButton() {
    if(this.state.mode === 'view') {
      return (
          <button onClick={this.handleEdit}>
            Edit
          </button>
      );
    } else {
      return( <button onClick={this.handleSave}> Save </button> ); }}render () {
    return (
      <div>
        <p>Text: {this.state.text}</p>
        {this.renderInputField()}
        {this.renderButton()}
      </div>
    );
  }
}

ReactDOM.render(
  <App />,
  document.getElementById('root'));Copy the code

The advantage of returning NULL to replace an empty element is that it will improve the performance of the build, since React does not have to unbind the component to replace it.

For example, when executing code that returns an empty div element, open the review page element and you will see how the div element below the following element is refreshed:

By contrast, when null is returned to hide the component, the div element is not updated when the Edit button is clicked:

Here, you’ll learn more about how React updates DOM elements and how the contrast algorithm works.

The performance improvement may be trivial in this simple example, but when you have a component that requires frequent updates, the situation is different.

The performance impact of conditional rendering will be discussed in more detail later. Now, let’s improve on this example.

The element variables

One thing I don’t like is having more than one return declaration in a method.

So I’ll use a variable to store the JSX element and initialize it only if the condition is judged true:

renderInputField() {
    let input;
    
    if(this.state.mode ! = ='view') {
      input = 
        <p>
          <input
            onChange={this.handleChange}
            value={this.state.inputText} />
        </p>;
    }
      
      return input;
  }
  
  renderButton() {
    let button;
    
    if(this.state.mode === 'view') {
      button =
          <button onClick={this.handleEdit}>
            Edit
          </button>;
    } else {
      button =
          <button onClick={this.handleSave}>
            Save
          </button>;
    }
    
    return button;
  }
Copy the code

Doing so is equivalent to methods that return NULL.

Here is the complete optimized code:

Babel + JSX:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {text: ' ', inputText: ' ', mode:'view'};
    
    this.handleChange = this.handleChange.bind(this);
    this.handleSave = this.handleSave.bind(this);
    this.handleEdit = this.handleEdit.bind(this);
  }
  
  handleChange(e) {
    this.setState({ inputText: e.target.value });
  }
  
  handleSave() {
    this.setState({text: this.state.inputText, mode: 'view'});
  }

  handleEdit() {
    this.setState({mode: 'edit'});
  }
  
  renderInputField() {
    let input;
    
    if(this.state.mode ! = ='view') {
      input = 
        <p>
          <input
            onChange={this.handleChange}
            value={this.state.inputText} />
        </p>;
    }
      
      return input;
  }
  
  renderButton() {
    let button;
    
    if(this.state.mode === 'view') {
      button =
          <button onClick={this.handleEdit}>
            Edit
          </button>;
    } else {
      button =
          <button onClick={this.handleSave}>
            Save
          </button>;
    }
    
    return button;
  }
  
  render () {
    return (
      <div>
        <p>Text: {this.state.text}</p>
        {this.renderInputField()}
        {this.renderButton()}
      </div>
    );
  }
}

ReactDOM.render(
  <App />,
  document.getElementById('root'));Copy the code

Using this approach makes the main render method more readable, but it may not be necessary to use if/else judgments (or statements like switch) and auxiliary render methods.

Let’s try an easier approach.

Ternary operator

We can use ternary operators instead of if/else statements:

condition ? expr_if_true : expr_if_false
Copy the code

The operator is wrapped in braces, and the expression can contain JSX, optionally enclosed in parentheses to improve readability.

It can be applied to different parts of a component. Let’s apply it to the example so that you can see the example.

I’ll remove the renderInputField and renderButton from the Render method and add a variable to indicate whether the component is in View or Edit mode:

render () {
  const view = this.state.mode === 'view';

  return (
      <div>
      </div>
  );
}
Copy the code

Now, you can use the ternary operator to return NULL if the component is set to View mode, and return the input field otherwise:

  // ...

  return (
      <div>
        <p>Text: {this.state.text}</p>
        
        {
          view
          ? null
          : (
            <p>
              <input
                onChange={this.handleChange}
                value={this.state.inputText} />
            </p>
          )
        }

      </div>
  );
Copy the code

Using the ternary operator, you can dynamically declare its buttons to save or edit by changing the component’s event handler and the actual tag text:

  // ...

  return (
      <div>
        <p>Text: {this.state.text}</p>
        
        {
          ...
        }

        <button
          onClick={
            view 
              ? this.handleEdit 
              : this.handleSave
          } >
              {view ? 'Edit' : 'Save'}
        </button>

      </div>
  );
Copy the code

As mentioned earlier, ternary operators can be applied to different parts of a component.

You can run it in fiddle to see the effect:

Jsfiddle.net/eh3rrera/y6…

And the operator.

In some special cases, the ternary operators can be simplified.

You can use the && operator when you want to render elements in one condition and not in another.

Unlike the & operator, && does not perform the judgment of the right expression when the left expression determines the final result.

For example, if the first expression is judged false (false &&…) , there is no need to perform the next expression because the result will always be false.

In React, you can use expressions like this:

return (
    <div>
        { showHeader && <Header /> }
    </div>
);
Copy the code

If showHeader is judged true, the

component will be returned by this expression.

If showHeader is judged false, the

component will be ignored and an empty div will be returned.

In this way, the following expressions are used:

{
  view
  ? null
  : (
    <p>
      <input
        onChange={this.handleChange}
        value={this.state.inputText} />
    </p>
  )
}
Copy the code

Can be rewritten as:

! view && ( <p> <input onChange={this.handleChange} value={this.state.inputText} /> </p> )Copy the code

Here is the complete code that can be executed in Fiddle:

Banel + JSX:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {text: ' ', inputText: ' ', mode:'view'};
    
    this.handleChange = this.handleChange.bind(this);
    this.handleSave = this.handleSave.bind(this);
    this.handleEdit = this.handleEdit.bind(this);
  }
  
  handleChange(e) {
    this.setState({ inputText: e.target.value });
  }
  
  handleSave() {
    this.setState({text: this.state.inputText, mode: 'view'});
  }

  handleEdit() {
    this.setState({mode: 'edit'});
  }
  
  render () {
    const view = this.state.mode === 'view';
    
    return( <div> <p>Text: {this.state.text}</p> { ! view && ( <p> <input onChange={this.handleChange} value={this.state.inputText} /> </p> ) } <button onClick={ view ? this.handleEdit : this.handleSave } > {view ?'Edit' : 'Save'}
        </button>
      </div>
    );
  }
}

ReactDOM.render(
  <App />,
  document.getElementById('root'));Copy the code

It looks better, doesn’t it?

However, ternary expressions don’t always look so good.

Consider a complex set of nested conditions:

return (
  <div>
    { condition1
      ? <Component1 />
      : ( condition2
        ? <Component2 />
        : ( condition3
          ? <Component3 />
          : <Component 4 />
        )
      )
    }
  </div>
);
Copy the code

This can get messy very quickly.

For this reason, sometimes you may want to use other techniques, such as executing functions immediately.

Execute function expressions now (IIFE)

As the name implies, immediate functions are functions that are called immediately after they are defined; they do not need to be called explicitly.

In general, you would define and execute a function like this:

function myFunction() {/ /... } myFunction();Copy the code

But if you want to execute a function immediately after it is defined, you must wrap the function with a pair of parentheses (to convert it into an expression) and execute it by adding two more parentheses (which can pass any arguments the function needs).

Something like this:

( function myFunction(/* arguments */) {
    // ...
}(/* arguments */) );
Copy the code

Or this:

( function myFunction(/* arguments */) {
    // ...
} ) (/* arguments */);
Copy the code

Since this function will not be called anywhere else, you can omit the function name:

( function (/* arguments */) {
    // ...
} ) (/* arguments */);
Copy the code

Or you can use the arrow function:

( (/* arguments */) => {
    // ...
} ) (/* arguments */);
Copy the code

In React, you can use curly braces to wrap the instant-execute function, write all the logic you want inside the function (if/else, switch, ternary operators, etc.), and then return whatever you want to render.

For example, the following execute now function is how to determine the render save or edit button logic:

{
  (() => {
    const handler = view 
                ? this.handleEdit 
                : this.handleSave;
    const label = view ? 'Edit' : 'Save';
          
    return( <button onClick={handler}> {label} </button> ); }}) ()Copy the code

Here is the complete code that can be executed in Fiddle:

Babel + JSX:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {text: ' ', inputText: ' ', mode:'view'};
    
    this.handleChange = this.handleChange.bind(this);
    this.handleSave = this.handleSave.bind(this);
    this.handleEdit = this.handleEdit.bind(this);
  }
  
  handleChange(e) {
    this.setState({ inputText: e.target.value });
  }
  
  handleSave() {
    this.setState({text: this.state.inputText, mode: 'view'});
  }

  handleEdit() {
    this.setState({mode: 'edit'});
  }
  
  render () {
    const view = this.state.mode === 'view';
    
    return( <div> <p>Text: {this.state.text}</p> { ! view && ( <p> <input onChange={this.handleChange} value={this.state.inputText} /> </p> ) } { (() => { const handler = view ? this.handleEdit : this.handleSave; const label = view ?'Edit' : 'Save';
          
            return (
              <button onClick={handler}>
                {label}
              </button>
            );
          })()
        }  
      </div>
    );
  }
}

ReactDOM.render(
  <App />,
  document.getElementById('root'));Copy the code
<div id="root"></div>
Copy the code

Child components

Many times, executing functions immediately may seem like a less elegant solution.

After all, we’re using React. React recommends splitting your application logic into as many components as possible, and recommends functional programming over imperative programming.

It would be a better solution to change the conditional rendering logic to a sub-component that decides how to render in different situations based on the props passed by the parent component.

But here, I’ll do something a little different and show you how to move from an imperative solution to more declarative and functional solutions.

I’ll start by creating a SaveComponent component:

const SaveComponent = (props) => {
  return (
    <div>
      <p>
        <input
          onChange={props.handleChange}
          value={props.text}
        />
      </p>
      <button onClick={props.handleSave}>
        Save
      </button>
    </div>
  );
};
Copy the code

Just like the properties of functional programming, the functional logic of SaveComponent comes from the parameters it receives. Define another EditComponent in the same way:

const EditComponent = (props) => {
  return (
    <button onClick={props.handleEdit}>
      Edit
    </button>
  );
};
Copy the code

Now the Render method looks like this:

render () {
    const view = this.state.mode === 'view';
    
    return (
      <div>
        <p>Text: {this.state.text}</p>
        
        {
          view
            ? <EditComponent handleEdit={this.handleEdit}  />
            : (
              <SaveComponent 
               handleChange={this.handleChange}
               handleSave={this.handleSave}
               text={this.state.inputText}
             />
            )
        } 
      </div>
    );
}
Copy the code

Here is the complete code that can be executed in Fiddle:

Babel + JSX:

const SaveComponent = (props) => {
  return (
    <div>
      <p>
        <input
          onChange={props.handleChange}
          value={props.text}
        />
      </p>
      <button onClick={props.handleSave}>
        Save
      </button>
    </div>
  );
};

const EditComponent = (props) => {
  return (
    <button onClick={props.handleEdit}>
      Edit
    </button>
  );
};

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {text: ' ', inputText: ' ', mode:'view'};
    
    this.handleChange = this.handleChange.bind(this);
    this.handleSave = this.handleSave.bind(this);
    this.handleEdit = this.handleEdit.bind(this);
  }
  
  handleChange(e) {
    this.setState({ inputText: e.target.value });
  }
  
  handleSave() {
    this.setState({text: this.state.inputText, mode: 'view'});
  }

  handleEdit() {
    this.setState({mode: 'edit'});
  }
  
  render () {
    const view = this.state.mode === 'view';
    
    return (
      <div>
        <p>Text: {this.state.text}</p>
        
        {
          view
            ? <EditComponent handleEdit={this.handleEdit}  />
            : (
              <SaveComponent 
               handleChange={this.handleChange}
               handleSave={this.handleSave}
               text={this.state.inputText}
             />
            )
        } 
      </div>
    );
  }
}

ReactDOM.render(
  <App />,
  document.getElementById('root'));Copy the code

If a component

There are libraries like JSX-Control-Statements that extend JSX to add conditional statements like:

<If condition={ true}> <span>Hi! </span> </If>Copy the code

These libraries provide more advanced components, but if we need simple if/else, we can refer to Michael J. Ryan’s comment under issue:

const If = (props) => {
  const condition = props.condition || false;
  const positive = props.then || null;
  const negative = props.else || null;
  
  returncondition ? positive : negative; }; / /...render () {
    const view = this.state.mode === 'view';
    const editComponent = <EditComponent handleEdit={this.handleEdit}  />;
    const saveComponent = <SaveComponent 
               handleChange={this.handleChange}
               handleSave={this.handleSave}
               text={this.state.inputText}
             />;
    
    return (
      <div>
        <p>Text: {this.state.text}</p>
        <If
          condition={ view }
          then={ editComponent }
          else={ saveComponent }
        />
      </div>
    );
}
Copy the code

Here is the complete code that can be executed in Fiddle:

Babel + JSX:

const SaveComponent = (props) => {
  return (
    <div>
      <p>
        <input
          onChange={props.handleChange}
          value={props.text}
        />
      </p>
      <button onClick={props.handleSave}>
        Save
      </button>
    </div>
  );
};

const EditComponent = (props) => {
  return (
    <button onClick={props.handleEdit}>
      Edit
    </button>
  );
};

const If = (props) => {
  const condition = props.condition || false;
  const positive = props.then || null;
  const negative = props.else || null;
  
  return condition ? positive : negative;
};

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {text: ' ', inputText: ' ', mode:'view'};
    
    this.handleChange = this.handleChange.bind(this);
    this.handleSave = this.handleSave.bind(this);
    this.handleEdit = this.handleEdit.bind(this);
  }
  
  handleChange(e) {
    this.setState({ inputText: e.target.value });
  }
  
  handleSave() {
    this.setState({text: this.state.inputText, mode: 'view'});
  }

  handleEdit() {
    this.setState({mode: 'edit'});
  }
  
  render () {
    const view = this.state.mode === 'view';
    const editComponent = <EditComponent handleEdit={this.handleEdit}  />;
    const saveComponent = <SaveComponent 
               handleChange={this.handleChange}
               handleSave={this.handleSave}
               text={this.state.inputText}
             />;
    
    return (
      <div>
        <p>Text: {this.state.text}</p>
        <If
          condition={ view }
          then={ editComponent }
          else={ saveComponent }
        />
      </div>
    );
  }
}

ReactDOM.render(
  <App />,
  document.getElementById('root'));Copy the code

High order component

HOC is a function that takes an existing component and returns a new component with additional functionality based on that component:

const EnhancedComponent = higherOrderComponent(component);
Copy the code

When applied to conditional rendering, a component is passed to a higher-order component that can return a different component from the original based on a number of conditions:

function higherOrderComponent(Component) {
  return function EnhancedComponent(props) {
    if (condition) {
      return<AnotherComponent { ... props } />; }return<Component { ... props } />; }; }Copy the code

Here’s an excellent article by Robin Wieruch on higher-order components, which delve into the use of higher-order components in conditional rendering.

In this article, I’ll borrow some EitherComponent concepts.

In functional programming, methods of the class Either are often implemented as a wrapper that returns two different values.

So let’s start by defining a function that takes two arguments, and another function that returns a Boolean (the result of judging the condition) that returns the component if true:

function withEither(conditionalRenderingFn, EitherComponent) {

}
Copy the code

Higher-order components usually start their function names with.

This function will return another function that takes an original component and returns a new one:

function withEither(conditionalRenderingFn, EitherComponent) {
    return function buildNewComponent(Component) {

    }
}
Copy the code

The component (function) returned by an inner function is the one you will use in your application, so it receives an object with all the properties it needs to run:

function withEither(conditionalRenderingFn, EitherComponent) {
    return function buildNewComponent(Component) {
        return function FinalComponent(props) {

        }
    }
}
Copy the code

ConditionalRenderingFn () returns EitherComponent (); conditionalRenderingFn () returns EitherComponent (); conditionalRenderingFn ();

function withEither(conditionalRenderingFn, EitherComponent) {
    return function buildNewComponent(Component) {
        return function FinalComponent(props) {
            return conditionalRenderingFn(props)
                ? <EitherComponent { ...props } />
                 : <Component { ...props } />;
        }
    }
}
Copy the code

Or use the arrow function:

const withEither = (conditionalRenderingFn, EitherComponent) => (Component) => (props) => conditionalRenderingFn(props) ? <EitherComponent { ... props } /> : <Component { ... props } />;Copy the code

In this way, using the original definition SaveComponent and EditComponent, you can create a withEditConditionalRendering high-order component, And through it you can create a EditSaveWithConditionalRendering components:

const isViewConditionFn = (props) => props.mode === 'view';

const withEditContionalRendering = withEither(isViewConditionFn, EditComponent);
const EditSaveWithConditionalRendering = withEditContionalRendering(SaveComponent);
Copy the code

This way you just need to use the component in the Render method and pass it all the attributes you need:

render () {    
    return (
      <div>
        <p>Text: {this.state.text}</p>
        <EditSaveWithConditionalRendering 
               mode={this.state.mode}
               handleEdit={this.handleEdit}
               handleChange={this.handleChange}
               handleSave={this.handleSave}
               text={this.state.inputText}
             />
      </div>
    );
}
Copy the code

Here is the complete code that can be executed in Fiddle:

Babel + JSX:

const SaveComponent = (props) => {
  return (
    <div>
      <p>
        <input
          onChange={props.handleChange}
          value={props.text}
        />
      </p>
      <button onClick={props.handleSave}>
        Save
      </button>
    </div>
  );
};

const EditComponent = (props) => {
  return( <button onClick={props.handleEdit}> Edit </button> ); }; const withEither = (conditionalRenderingFn, EitherComponent) => (Component) => (props) => conditionalRenderingFn(props) ? <EitherComponent { ... props } /> : <Component { ... props } />; const isViewConditionFn = (props) => props.mode ==='view';

const withEditContionalRendering = withEither(isViewConditionFn, EditComponent);
const EditSaveWithConditionalRendering = withEditContionalRendering(SaveComponent);

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {text: ' ', inputText: ' ', mode:'view'};
    
    this.handleChange = this.handleChange.bind(this);
    this.handleSave = this.handleSave.bind(this);
    this.handleEdit = this.handleEdit.bind(this);
  }
  
  handleChange(e) {
    this.setState({ inputText: e.target.value });
  }
  
  handleSave() {
    this.setState({text: this.state.inputText, mode: 'view'});
  }

  handleEdit() {
    this.setState({mode: 'edit'});
  }
  
  render () {    
    return (
      <div>
        <p>Text: {this.state.text}</p>
        <EditSaveWithConditionalRendering 
               mode={this.state.mode}
               handleEdit={this.handleEdit}
               handleChange={this.handleChange}
               handleSave={this.handleSave}
               text={this.state.inputText}
             />
      </div>
    );
  }
}

ReactDOM.render(
  <App />,
  document.getElementById('root'));Copy the code

Performance considerations

Conditional rendering can be complex. As I showed earlier, the performance of each approach may be different.

However, most of the time the difference is not a problem. But when it does cause problems, you’ll need to dig into how the React virtual DOM works and use a few tricks to optimize performance.

Here’s a great article on optimizing React conditional rendering that I highly recommend you read.

The basic idea is that conditional rendering resulting in changing the location of components will cause backflow, resulting in unbinding/binding of components within the application.

Based on the examples in this article, I wrote two examples:

The first example uses if/else to control the show/hide of the SubHeader component:

Babel + JSX:

const Header = (props) => {
  return <h1>Header</h1>;
}

const Subheader = (props) => {
  return <h2>Subheader</h2>;
}

const Content = (props) => {
  return <p>Content</p>;
}

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};
    
    this.handleClick = this.handleClick.bind(this);
  }
  
  handleClick() { this.setState(prevState => ({ isToggleOn: ! prevState.isToggleOn })); }render() {
    if(this.state.isToggleOn) {
      return (
        <div>
          <Header />
          <Subheader /> 
          <Content />
          <button onClick={this.handleClick}>
            { this.state.isToggleOn ? 'ON' : 'OFF' }
          </button>
        </div>
      );
    } else {
      return (
        <div>
          <Header />
          <Content />
          <button onClick={this.handleClick}>
            { this.state.isToggleOn ? 'ON' : 'OFF' }
          </button>
        </div>
      );
    }
  }
}

ReactDOM.render(
    <App />,
  document.getElementById('root'));Copy the code

The second example uses and to do the same thing:

Babel + JSX:

const Header = (props) => {
  return <h1>Header</h1>;
}

const Subheader = (props) => {
  return <h2>Subheader</h2>;
}

const Content = (props) => {
  return <p>Content</p>;
}

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};
    
    this.handleClick = this.handleClick.bind(this);
  }
  
  handleClick() { this.setState(prevState => ({ isToggleOn: ! prevState.isToggleOn })); }render() {
    return (
      <div>
        <Header />
        { this.state.isToggleOn && <Subheader /> }
        <Content />
        <button onClick={this.handleClick}>
          { this.state.isToggleOn ? 'ON' : 'OFF' }
        </button>
      </div>
    );
  }
}

ReactDOM.render(
    <App />,
  document.getElementById('root'));Copy the code

Open element inspection and click the button a few times.

You’ll see how Content is handled in each implementation.

conclusion

As with many things in programming, there are many ways to implement conditional rendering in React.

I would say that you can choose any method you like other than the first (there are multiple if/else returns).

You can decide which approach is best for your situation based on the following principles:

  • Your programming style
  • The complexity of conditional logic
  • Comfort with JavaScript, JSX, and advanced React concepts such as higher-order components.

If all things are equal, aim for simplicity and readability.


Plug: LogRocket, a DVR for web apps

LogRocket is a front-end logging tool that can reproduce problems on your own browser. Instead of trying to guess why something went wrong or asking the user for screenshots and logs, LogRocket helps you replay the scene to quickly understand what went wrong. It works with any application, is framework-independent, and has plug-ins that record other contexts from Redux, Vuex, and @ngrx/ Store.

In addition to logging Redux actions and states, LogRocket logs console logs, JavaScript errors, stack traces, network requests/responses with headers + bodies, browser metadata, and custom logs. It can also detect the DOM to record HTML and CSS on a page, and even the most complex single-page applications can be restored to pixel-level video.

Free trial.

If you find any mistakes in your translation or other areas that need to be improved, you are welcome to the Nuggets Translation Program to revise and PR your translation, and you can also get the corresponding reward points. The permanent link to this article at the beginning of this article is the MarkDown link to this article on GitHub.


The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.