【问题标题】:How to extract JSX from class render method?如何从类渲染方法中提取 JSX?
【发布时间】:2017-11-30 17:30:27
【问题描述】:

我想为 React Native 编写类似 SCSS 的东西:它会解析你的组件 jsx 和特殊的类似 SCSS 的样式,并返回一个带有重新设计的样式和 jsx 的普通 RN 组件。

假设我们有这个反应代码:

class MyClass extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Text>I remember syrup sandwiches</Text>
      </View>
    );
  }
}

我还有 SCSS-ish 样式,其中每个带有容器“类”的父组件中的 Text 组件都将具有我们提供的相同道具。

const styles = StyleSheet.create(
  toRNStyles({
    container: {
      Text: { color: 'red' },
    },
  })
);

最后我们需要这样的输出:

...
<View style={styles.container}>
  <Text style={styles._Text_container}>
    I remember syrup sandwiches
  </Text>
</View>
...

那么如何从类外部获取从 render 方法返回的 jsx?

【问题讨论】:

  • 出于什么目的需要输出?如果您需要在运行时进行调整,您可以按照@yoda 的建议使用render(),或者使用高阶组件包装组件。此外,输出不是 JSX(这是使用组件的语法),而是一个 React Element(它是一个描述组件实例的对象)
  • 为什么要这样做?提供一些上下文。可能这不是您想要解决的问题。
  • 您希望 jsx 作为字符串文字?
  • @Chris 是的,所以我可以解析它。

标签: javascript reactjs react-native


【解决方案1】:

您可以为 babel 编写一个插件,因为 react-native 使用它来将 JSX 转换为纯 javascript。 看看这些包:

  • babel-helper-builder-react-jsx
  • babel-plugin-syntax-jsx
  • babel-plugin-transform-react-jsx
  • babel-plugin-transform-react-jsx-source
  • jsx-ast-utils

【讨论】:

    【解决方案2】:

    似乎没有标准的方法来做到这一点。但是,您可以导入 ReactDOMServer 并使用其renderToStaticMarkup 函数。

    像这样:

    class MyApp extends React.Component {
      render() {
        var myTestComponent = <Test>bar</Test>;
        console.dir(ReactDOMServer.renderToStaticMarkup(myTestComponent));
        
        return myTestComponent;
      }
    }
    
    const Test = (props) => {
      return (
        <div>
          <p>foo</p>
          <span>{props.children}</span>
        </div>
      );
    }
    
    ReactDOM.render(<MyApp />, document.getElementById("myApp"));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom-server.js"></script>
    <div id="myApp"></div>

    【讨论】:

      【解决方案3】:

      我认为解析返回的元素是错误的方法。一个挑战是style 的值将是一个对象(styles.container === 样式键/值的哈希),而您需要一个可以映射到对象的键。

      我认为最可重用的方法是利用 React 上下文(我假设 RN 支持!)来构建一个 styleName,当你从组件树中下来时可以对其进行扩充。

      这是一种初始方法,它做出了一些假设(例如,每个组件都将 styleName 作为道具提供;您可能希望在设计时而不是运行时提供它)。简而言之,你用这个 HOC 包装你想要参与的每个组件,并提供 styleName 作为每个组件的道具。将这些 styleName 值连接起来生成映射到样式的上下文化名称。

      这个例子产生:

      <div style="background-color: green; color: red;">
        <div style="color: blue;">Some Text</div>
      </div>
      

      const CascadingStyle = (styles, Wrapped) => class extends React.Component {
        static displayName = 'CascadingStyle';
        
        static contextTypes = {
          styleName: React.PropTypes.string
        }
        
        static childContextTypes = {
          styleName: React.PropTypes.string
        }
        
        // pass the current styleName down the component tree
        // to other instances of CascadingStyle
        getChildContext () {
          return {
            styleName: this.getStyleName()
          };
        }
      
        // generate the current style name by either using the
        // value from context, joining the context value with
        // the current value, or using the current value (in
        // that order).
        getStyleName () {
          const {styleName: contextStyleName} = this.context;
          const {styleName: propsStyleName} = this.props;
          let styleName = contextStyleName;
      
          if (propsStyleName && contextStyleName) {
            styleName = `${contextStyleName}_${propsStyleName}`;
          } else if (propsStyleName) {
            styleName = propsStyleName;
          }
      
          return styleName;
        }
        
        // if the component has styleName, find that style object and merge it with other run-time styles
        getStyle () {
          if (this.props.styleName) {
              return Object.assign({}, styles[this.getStyleName()], this.props.styles);
          }
      
          return this.props.styles;
        }
        
        render () {
          return (
            <Wrapped {...this.props} style={this.getStyle()} />
          );
        }
      };
      
      const myStyles = {
        container: {backgroundColor: 'green', color: 'red'},
        container_text: {color: 'blue'}
      };
      
      const Container = CascadingStyle(myStyles, (props) => {
        return (
          <div {...props} />
        );
      });
      
      const Text = CascadingStyle(myStyles, (props) => {
        return (
          <div {...props} />
        );
      });
      
      const Component = () => {
        return (
          <Container styleName="container">
            <Text styleName="text">Some Text</Text>
          </Container>
        );
      };
      <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

      【讨论】:

        猜你喜欢
        • 2018-10-01
        • 2020-06-28
        • 2018-01-02
        • 2019-03-21
        • 2021-11-29
        • 1970-01-01
        • 2017-10-12
        • 2018-07-19
        • 2021-07-31
        相关资源
        最近更新 更多