React router v4渲染方法重构

时间:2017-05-16 08:37:55

标签: react-router react-jsx react-router-v4

我有一个运作良好的组件。但我想改变代码,所以 React.createElement(component, { ...props, loggingIn, authenticated }) 将使用 JSX 编写。

import React, { PropTypes } from 'react';
import { Route, Redirect } from 'react-router-dom';

const Public = ({ loggingIn, authenticated, component, ...rest }) => (
  <Route {...rest} render={(props) => {
    if (loggingIn) return <div></div>;
    return !authenticated ?
    (React.createElement(component, { ...props, loggingIn, authenticated })) :
    (<Redirect to="/documents" />);
  }} />
);

Public.propTypes = {
  loggingIn: PropTypes.bool,
  authenticated: PropTypes.bool,
  component: PropTypes.func,
};

export default Public;

1 个答案:

答案 0 :(得分:1)

只需对Public组件的参数稍作更改即可完成此操作。

const Public = ({ component: Component, loggingIn, authenticated, ...rest }) => (

我花了一些实验来了解如何捕获组件参数以便将其用作JSX ,因此我使用下面的代码创建了working example on Codepen。 (注意我使用HashRouter因为BrowserRouter在Codepen中不起作用。忽略它 - 对解决方案不重要。我还删除了Public组件中的一些逻辑关注解决方案。在最终的代码中,您当然可以将loggingInauthenticated逻辑添加回Public组件。)

const {
  HashRouter,
  Route,
  Switch,
  Link
} = ReactRouterDOM

const Home = () => (
  <span>Home</span>
);

const Another = (props) => (
  <div>
    <span>Another</span>
    <div>Bah: {props.bah}</div>
  </div>
);

const NotHome = (props) => {    
  return (
    <div>
      <div>Not Home</div>
      <div>Logging in: {props.loggingIn}</div>
      <div>Authenticated: {props.authenticated}</div>
      <div>Other prop: {props.otherProp}</div>
      <div>Route props(match.path): {props.match.path}</div>
    </div>
  );
};

const Public = ({ component: Component, loggingIn, authenticated, ...rest }) => (
  <Route {...rest} render={(props) => {
    // return (React.createElement(Component, { ...props, loggingIn, authenticated }));
    return (<Component {...props} {...rest} loggingIn={loggingIn} authenticated={authenticated} />);
  }} />
);

ReactDOM.render((
  <HashRouter>
    <div>
      <ul>
        <li><Link to="/">Home</Link></li>
        <li><Link to="/public">Public</Link></li>
        <li><Link to="/another">Another</Link></li>
      </ul>

      <p>The rendered route component:{' '}
        <Switch>
          <Route exact path="/" component={Home} />
          <Public path="/public" component={NotHome} loggingIn="could be" authenticated="perhaps" otherProp="bar" />
          <Public path="/another" component={Another} loggingIn="no..." authenticated="not" bah="baz" />
        </Switch>
      </p>
    </div>
  </HashRouter>
), document.getElementById('app'));

我认为理解这一点的关键是将how Babel transforms the JSX看到createElement,您会注意到:

<Public component={MyComponent} />

变换为:

React.createElement(Public, { component: MyComponent });

基本部分为{ component: MyComponent },因此您的参数需要更改。但是等等,这没有多大意义。因为{ component }的参数与{ component: component }相同。请参阅MDN docs on destructuring assignment syntax,重要部分为Assignment without declarationAssigning to new variable names

所以在我的工作示例中真正发生的唯一事情是变量名现在大写了...... Wat?

深入挖掘,using Babel some more,真正的原因是JSX处理小写和大写组件名称的方式。比较:

console.log(<component />);

// Which is transformed to:
console.log(React.createElement("component", null));

使用:

console.log(<Component />);

// Transformed to:
console.log(React.createElement(Component, null));

您需要使用大写来获取变量名而不是字符串。 (这导致了另一种解决方案,但这意味着将属性名称大写,这不是标准的,因此我没有提供该解决方案。)

事实上,React JSX In Depth docs简要提到了如何处理大写(强调我的):

&#34; JSX标记的第一部分确定了React元素的类型。

大写类型表示JSX标记指的是React组件。 这些标记被编译成对命名变量的直接引用,因此如果使用JSX <Foo />表达式,Foo必须在范围内。&#34;

还有User-Defined Components Must Be Capitalized部分:

&#34;当元素类型以小写字母开头时,它引用内置组件,如<div><span>,并生成字符串'div'或{{ 1}}传递给'span'。以大写字母开头的类型如React.createElement编译为<Foo />,并对应于JavaScript文件中定义或导入的组件。

我们建议使用大写字母命名组件。如果你有一个以小写字母开头的组件,在将它用于JSX之前将其分配给大写变量。&#34;

上面的链接继续展示一些有用的例子。