Различные способы [Перевод] React для достижения условий рендеринга и соображений производительности

внешний интерфейс Программа перевода самородков React.js
Различные способы [Перевод] React для достижения условий рендеринга и соображений производительности

JSX).

如果你想要遍历一个列表来渲染多个组件或者实现一些条件逻辑,你不得不使用纯 Javascript,你也并没有很多的选择来处理循环。 Чаще,mapудовлетворит ваши потребности.

Но как насчет условных выражений?

Это другая история.

Есть несколько вариантов на ваш выбор

В React есть несколько способов использования условных операторов. И, как и в большинстве случаев в программировании, в зависимости от реальной проблемы, которую вы пытаетесь решить, некоторые подходы являются более подходящими.

В этом руководстве рассматриваются наиболее популярные методы условного рендеринга:

  • If/Else
  • Избегайте рендеринга пустых элементов
  • переменная элемента
  • Тернарный оператор
  • А ТАКЖЕ (&&)
  • Немедленный вызов функции (IIFE)
  • Подсборка
  • Компонент высшего порядка (HOC)

В качестве примера того, как работают все эти методы, далее будут реализованы компоненты с функциями просмотра/редактирования:

ты сможешьJSFiddleПопробуйте разветвить все примеры в .

Давайте начнем с самой примитивной реализации с использованием if/else и построим ее здесь.

If/else

Давайте создадим компонент со следующим состоянием:

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

Вы будете использовать одно свойство для хранения текста, а другое свойство — для хранения редактируемого текста. Третий атрибут будет использоваться для указания того, что вы находитесь вeditещеviewрежим.

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'});
  }
}

Теперь для метода рендеринга в дополнение к сохраненному тексту проверьте свойство модального состояния, чтобы отобразить кнопку редактирования или поле ввода текста и кнопку сохранения:

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'});
  }
}

Вот полный код, вы можете попробовать его в скрипке:

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')
);

Если / else самый простой способ решить это, но я уверен, что вы знаете, что это не хороший способ сделать это.

Это работает для простых случаев использования, и каждый программист знает, как это работает. Но есть много повторений,render

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>
    );
  }
}

Вот полный код, вы можете попробовать его в скрипке:

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')
);

renderInputFieldвернулся пустойdivэлемент.

Однако это не нужно.

Избегайте рендеринга пустых элементов

Если хочешьСпрятатькомпонент, вы можете вернуть его метод рендерингаnullПотому что нет необходимости рендерить пустой (и другой) элемент на место.

null

Например, между двумя компонентами реализован следующий счетчик кода:

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')
);

NumberКомпонент отображает значение, переданное родительским компонентом, только если родительский компонент передает четное число, в противном случае он вернетnull. Затем, глядя на вывод консоли, вы увидите, что независимо отrenderчто вернуть,componentDidUpdateвсегда будут вызывать.

Возвращаясь к нашему примеру, измените его следующим образом.renderInputFieldметод:

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

Вот полный код:

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')
);

вернутьnullПреимущество замены пустого элемента заключается в том, что это немного улучшит производительность компонента, поскольку React не нужно отвязывать компонент, чтобы заменить его.

Например, при выполнении возврата пустымdivКогда код элемента открыт, откройте элемент страницы проверки, и вы увидитеdivКак элемент обновляется:

В отличие от этого примера, при возвратеnullскрыть компоненты,Editкогда кнопка нажатаdivЭлементы не обновляются:

здесь, вы узнаете больше о том, как React обновляет элементы DOM и как работает алгоритм «сравнения».

Может быть, в этом простом примере повышение производительности тривиально, но когда в компоненте, который нужно часто обновлять, ситуация будет другой.

Последствия условного рендеринга для производительности подробно обсуждаются позже. Теперь давайте перейдем к улучшению этого примера.

переменная элемента

Одна вещь, которую я не люблю, это наличие более одного метода в одном методе.returnутверждение.

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;
  }

nullМетод.

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')
);

Используйте этот способ, чтобы сделать основнойrenderметод более удобочитаем, но, возможно, нет необходимости использовать тест if/else (или что-то вродеswitchтакие операторы) и вспомогательные методы рендеринга.

Попробуем более простой способ.

Тернарный оператор

мы можем использоватьТернарный операторвместо оператора if/else:

condition ? expr_if_true : expr_if_false

Оператор заключен в фигурные скобки, а выражения могут содержать JSX, опционально заключенные в круглые скобки для удобства чтения.

Он может быть применен к различным частям компонента. Давайте применим это к примеру, чтобы вы могли увидеть это в действии.

я буду вrenderметод удаленrenderInputFieldа такжеrenderButtonи добавьте переменную, чтобы указать, что компонент находится вviewещеeditмодель:

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

  return (
      <div>
      </div>
  );
}

Теперь вы можете использовать Ternary Operator, когда узел предоставленviewВернуться в режимnull

  // ...

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

      </div>
  );

  // ...

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

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

      </div>
  );

Вы можете увидеть эффекты в эксплуатации Widdle в:

JS fiddle.net/worst 3 soft и ah/has 6…

И оператор

В частном случае тернарный оператор можно упростить.

Когда вы хотите сделать элемент в одном состоянии, а не в другом, вы можете использовать&&оператор.

отличный от&оператор, когда выражение слева может определить окончательный результат,&&Решение правого выражения больше не будет выполняться.

Например, если первое выражение оценивается как false (false && …), нет необходимости вычислять следующее выражение, потому что результат всегда будетfalse.

В React вы можете использовать такие выражения:

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

еслиshowHeaderсчитаетсяtrue,<Header/>Компонент будет возвращен этим выражением.

еслиshowHeaderсчитаетсяfalse,<Header/>divЭто будет возвращено.

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

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

Вот полный код, исполняемый в скрипте:

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')
);

Выглядит лучше, не так ли?

Однако тернарные выражения не всегда выглядят так хорошо.

Рассмотрим сложный набор вложенных условий:

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

Это может быстро запутаться.

По этой причине иногда вы можете захотеть использовать другие методы, такие как немедленное выполнение функции.

Выражение немедленного выполнения функции (IIFE)

Как следует из названия, непосредственные функции — это функции, которые вызываются сразу после их определения, их не нужно вызывать явно.

Обычно вы определяете и выполняете (выполняете после определения) такую ​​функцию:

function myFunction() {

// ...

}

myFunction();

Но если вы хотите выполнить функцию сразу после ее определения, вы должны обернуть функцию парой круглых скобок (преобразовав ее в выражение) и выполнить ее, добавив еще две круглые скобки (внутри круглых скобок вы можете передать то, что нужно функции любые параметры).

так:

( function myFunction(/* arguments */) {
    // ...
}(/* arguments */) );

или это:

( function myFunction(/* arguments */) {
    // ...
} ) (/* arguments */);

Поскольку эта функция больше нигде не будет вызываться, имя функции можно опустить:

( function (/* arguments */) {
    // ...
} ) (/* arguments */);

Или вы также можете использовать функции стрелок:

( (/* arguments */) => {
    // ...
} ) (/* arguments */);

В React вы можете использовать фигурные скобки, чтобы обернуть немедленно выполняемую функцию, написать всю необходимую логику внутри функции (если/иначе, переключатель, тернарные операторы и т. д.) и вернуть все, что вы хотите отобразить.

Например, следующая функция немедленного выполнения — это логика того, как определить, отображать ли кнопку сохранения или редактирования:

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

Вот полный код может быть выполнен в скрипке в:

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')
);
<div id="root"></div>

Подсборка

В конце концов, мы используем React, рекомендуемое решение React состоит в том, чтобы разбить логику вашего приложения на максимальное количество компонентов, и рекомендуется функциональное программирование, а не императивное программирование.

Поэтому измените логику условного рендеринга в качестве дочернего компонента, этот дочерний компонент будет определять рендеринг в разных ситуациях в соответствии с реквизитами, переданными родительским компонентом, что будет лучшим решением.

Но здесь я собираюсь сделать кое-что немного другое и показать вам, как перейти от императивного решения к более декларативному и функциональному решению.

я создамSaveComponentКомпоненты начинаются:

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

Как функциональные атрибуты программирования,SaveComponentФункциональная логика функции выводится из параметров, заданных параметрами, которые она получает. Таким же образом определите другой компонентEditComponent:

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

СейчасrenderМетод становится таким:

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>
    );
}

Вот полный код, который можно выполнить в скрипте:

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')
);

Если компоненты

подобноjsx-control-statementsТакие библиотеки могут расширять JSX, добавляя следующее:

<If condition={ true }>

  <span>Hi!</span>

</If>

Эти библиотеки предоставляют более продвинутые компоненты, но если нам нужны простые if/else, мы можем обратиться кMichael J. RyanсуществуетissueвнизКомментарий:

const If = (props) => {
  const condition = props.condition || false;
  const positive = props.then || null;
  const negative = props.else || null;
  
  return condition ? 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>
    );
}

Вот полный код, который можно выполнить в скрипте:

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')
);

компоненты более высокого порядка

компоненты более высокого порядка(HOC) — это функция, которая берет существующий компонент и возвращает новый компонент с дополнительными функциями на основе этого компонента:

const EnhancedComponent = higherOrderComponent(component);

При применении к условию рендеринга компонент более высокого порядка передается компоненту, компонент более высокого порядка может собираться в соответствии с некоторыми исходными компонентами, чтобы вернуть условие, отличное от:

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

    return <Component { ...props } />;
  };
}

Вот статьяRobin WieruchнаписаноЗамечательная хорошая статья о компонентах высокого порядка

EitherComponentКонцепция чего-либо.

Either

Итак, давайте начнем с определения функции, которая принимает два аргумента, и еще одной функции, которая возвращает логическое значение (результат условия), если логическое значение равноtrueзатем возвращает компонент:

function withEither(conditionalRenderingFn, EitherComponent) {

}

Обычно имя функции компонентов высокого порядкаwithначало.

Эта функция вернет другую функцию, которая берет исходный компонент и возвращает новый компонент:

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

    }
}

Этот компонент (функция), возвращаемый внутренней функцией, — это то, что вы будете использовать в своем приложении, поэтому он получает объект со всеми свойствами, необходимыми для работы:

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

        }
    }
}

Внутренняя функция может получить доступ к параметрам внешней функции, поэтому в соответствии с функциейconditionalRenderingFnEitherComponentComponent:

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

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

Кстати, используйте оригинальное определениеSaveComponentа такжеEditComponentВы можете создатьwithEditConditionalRenderingКомпоненты более высокого порядка, и с помощью которых можно создатьEditSaveWithConditionalRenderingКомпоненты:

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

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

Таким образом, вы просто используете компонент в методе рендеринга и передаете ему все необходимые свойства:

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>
    );
}

Вот полный код, который можно выполнить в скрипте:

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')
);

Вопросы производительности

Условный рендеринг может быть сложным. Как я показал ранее, производительность каждого подхода также может быть разной.

Однако в большинстве случаев эта разница не является проблемой. Но когда это действительно вызывает проблемы, вам нужно иметь четкое представление о том, как работает виртуальный DOM React, и использовать несколько приемов, чтобыОптимизация производительности.

Вот хорошая статья оОптимизация условного рендеринга React, очень рекомендую прочитать.

Основная идея заключается в том, что условный рендеринг, приводящий к изменению положения компонента, вызовет перекомпоновку, что приведет к отвязке/привязке компонентов в приложении.

На основе примеров из этого поста я написал два примера:

В первом примере используется if/else для управленияSubHeader

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')
);

&&

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')
);

Content

В заключение

Как и многие вещи в программировании, существует много способов реализации условного рендеринга в реакцию.

Я бы сказал, что кроме первого способа (существует несколько возвратов if/else) вы можете делать все, что захотите.

Основываясь на следующих принципах, вы можете решить, какой подход лучше всего подходит в вашей реальной ситуации:

  • ваш стиль программирования
  • Условная логическая сложность
  • Удобство работы с JavaScript, JSX и передовыми концепциями React, такими как компоненты высшего порядка.

Если все равны, стремитесь к простоте и удобочитаемости.


Plug: LogRocket, a DVR for web apps

LogRocketЭто интерфейсный инструмент ведения журнала, который может воспроизвести проблему в вашем собственном браузере. Вместо того, чтобы гадать, почему произошла ошибка, или пользователь хочет захватить и зарегистрировать, LogRocket поможет вам заново пережить короля, чтобы быстро понять, что пошло не так. Он подходит для любого приложения, и не зависит от фрейма и, и имеет от Redux, Vuex и @ngrx/store записи других контекстов plug.

Бесплатная пробная версия.

Если вы обнаружите ошибки в переводе или в других областях, требующих доработки, добро пожаловать наПрограмма перевода самородковВы также можете получить соответствующие бонусные баллы за доработку перевода и PR. начало статьиПостоянная ссылка на эту статьюЭто ссылка MarkDown этой статьи на GitHub.


Программа перевода самородковэто сообщество, которое переводит высококачественные технические статьи из Интернета сНаггетсДелитесь статьями на английском языке на . Охват контентаAndroid,iOS,внешний интерфейс,задняя часть,блокчейн,товар,дизайн,искусственный интеллектЕсли вы хотите видеть более качественные переводы, пожалуйста, продолжайте обращать вниманиеПрограмма перевода самородков,официальный Вейбо,Знай колонку.