在非常相似的组件的情况下,进行条件渲染的最佳方法是啥?

Posted

技术标签:

【中文标题】在非常相似的组件的情况下,进行条件渲染的最佳方法是啥?【英文标题】:What is the best way to go about conditional rendering in the case of very similar components?在非常相似的组件的情况下,进行条件渲染的最佳方法是什么? 【发布时间】:2018-11-24 08:09:15 【问题描述】:

我根据用户是教授、学生还是未登录来呈现不同的登录页面。登录页面非常相似;唯一的区别是显示的按钮。我知道我可以使用内联条件或简单的 if-else 语句来解决这个问题。但是,我想知道在这种情况下实现条件渲染的最佳 实践是什么。我知道高阶组件 (HOC) 可以提供帮助,但我不确定它们在这种特殊情况下是否过大。

为了在同一个页面上,这里是我目前使用 if-else 语句呈现的不同着陆组件。

Landing.js(未登录用户):

import React from 'react';
import  withRouter  from 'react-router-dom';
import  compose  from 'recompose';
import  withEither  from '../../helpers/withEither';
import LandingStudent from './LandingStudent';
import LandingProfessor from './LandingProfessor';
import './Landing.css';

const Landing = ( history ) => 
  return(
    <div className="header">
      <div className="text-box">
        <h1 className="header-primary">
          <span className="header-primary-main">
            QME
          </span>
          <span className="header-primary-sub">
            the best way to ask questions
          </span>
        </h1>
      </div>
    </div>
  );
;

export default Landing;

LandingProfessor.js

import React from 'react';
import  withRouter  from 'react-router-dom';
import RaisedButton from 'material-ui/RaisedButton';
import './Landing.css';

const LandingProfessor = ( history ) => 
  return(
    <div className="header">
      <div className="text-box">
        <h1 className="header-primary">
          <span className="header-primary-main">
            QME
          </span>
          <span className="header-primary-sub">
            the best way to ask questions
          </span>
        </h1>
        <RaisedButton
          className="btn-animated btn-landing"
          label="Create Class"
          onClick=() => history.push('/courses/new')
        />
        <RaisedButton
          className="btn-animated btn-landing"
          label="Dashboard"
          onClick=() => history.push('/courses')
        />
      </div>
    </div>
  );
;

export default withRouter(LandingProfessor);

LandingStudent.js

import React from 'react';
import  withRouter  from 'react-router-dom';
import RaisedButton from 'material-ui/RaisedButton';
import './Landing.css';

const Landing = ( history ) => 
  return(
    <div className="header">
      <div className="text-box">
        <h1 className="header-primary">
          <span className="header-primary-main">
            QME
          </span>
          <span className="header-primary-sub">
            the best way to ask questions
          </span>
        </h1>
        <RaisedButton
          className="btn-animated btn"
          label="Join Class"
          onClick=() => history.push('/courses/join')
        />
      </div>
    </div>
  );
;

export default withRouter(Landing);

【问题讨论】:

使用if/elseswitch 似乎是合理的。你有什么具体的顾虑?在您可以测量并证明在这些组件之间切换实际上是一个性能问题之前,我不会担心“过多的重新渲染”。 @RossAllen 我担心的是我对我的 Header 组件以及将来可能的其他组件做同样的事情。我想知道是否有办法做到这一点,而不必重复相同的 if/else 或 switch 语句,并使我的代码尽可能枯燥。 您能否添加一些您关心的条件渲染或用该代码打开另一个问题并提及我? 【参考方案1】:

我不认为在这种特殊情况下临时性是矫枉过正。 我认为只要您可以使用 hoc 以获得更好的可读性和可用性,就使用它。

使用一些recompose hocs(你应该安装recompose:npm install recompose

export const renderByConditions = (...conditions) => compose(
  ...conditions.map(condition =>
    branch(condition[0], (condition[1] && renderComponent(condition[1])) || renderNothing, condition[2] && renderComponent(condition[2]))
  )
)

你应该传递带有签名的数组:

[conditionleft(如果条件为真),right(否则)]

condition - (ownProps) => youCondition
left - Component | empty
right - Component | empty

left 为空时 - 如果条件为真,则呈现 null。

right 为空时 - 如果条件不成立,则会渲染我们包装的组件

然后你可以使用 hoc:

renderByConditions(
  [props => props.landing, props => <Landing ...props/>],
  [props => props.proffesor, LandingProfessor],
  [props => props.student, LandingStudent],
  [Component => Component, DefaultComponent]
)

我建议开始使用 recompose hocs,它们很棒!

【讨论】:

【参考方案2】:

我会在这里选择组合而不是继承,这意味着创建许多可重用的组件并用那些可重用的组件组合您的***组件,而不是让您的***组件继承自一种常见的组件类型。如果您采用后一种方法,您最终可能会得到一份我认为比您现在拥有的更好的道具清单。

首先,您可以将标题组件化:

Header.js

export default function Header(props) 
  return (
    <h1 className="header-primary">
      props.children
    </h1>
  );


Header.Main = function HeaderMain(props) 
  return (
    <span className="header-primary-main">
      props.children
    </span>
  );
;

Header.Sub = function HeaderSub(props) 
  return (
    <span className="header-primary-sub">
      props.children
    </span>
  );
;

并在 Landing.js 中使用它:

import Header from './Header.js';

const Landing = ( history ) => 
  return(
    <div className="header">
      <div className="text-box">
        <Header>
          <Header.Main>QME</Header.Main>
          <Header.Sub>the best way to ask questions</Header.Sub>
        </Header>
      </div>
    </div>
  );

【讨论】:

【参考方案3】:

一个“技巧”可能是在根 div 上附加一个 className 用于景观,即“学生”、“教授”或“注销”,并使用 css display: none 在每个场景中隐藏不需要的项目

另一种方法是保留您的 3 个组件,但将所有渲染委托给一个通用的“LandingRenderer”组件,该组件将接受诸如“showJoinClassButton”、“showCreateButtonButton”等布尔属性。

然后你的 3 个组件渲染看起来像这样

LandingProfessor: (props) => (
    <LandingRenderer 
        showJoinClassButton=false 
        showCreateClassButton=true 
        ... 
        ...props />
)

【讨论】:

以上是关于在非常相似的组件的情况下,进行条件渲染的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

ReactJS 使用 props 条件渲染组件的最佳方式

React条件渲染

在vue中让某个组件重新渲染的笨方法

Rails:分离两个非常相似的视图的最佳方法是啥?

React 组件性能优化探索实践

如何对过滤器方法的结果进行条件渲染?反应