解析组件映射到新组件

时间:2018-03-02 00:12:36

标签: reactjs

我试图找出如何将react组件作为输入并映射到新的组件树(替换标签并根据需要进行修改)。在这种特定情况下,我想为DOM输出编写一个组件,并将其修改为本机输出。

因此,非常简化的组件可能来自:

<MyComponent>
  <p>foo</p>
</MyComponent>

为:

<MyComponent>
  <Text>foo</Text>
</MyComponent>

修改单个标签道具的加分点,例如添加style道具,事件处理程序等等。我知道如果有一个答案,那么它不会是一刀切的。我只是希望对遇到这个用例的一般方法/其他人提出一些指导并将其推进。

1 个答案:

答案 0 :(得分:0)

这样的事情似乎运作得相当好,尽管在树下传递样式是一个悬而未决的问题。好吧,至少我们知道从一组标签到另一组标签的转换是可能的。道具https://stackoverflow.com/users/2578335/giles-copp

// traverse.js
import React from 'react';

export function kindOf(node) {
  if (node === null || node === undefined || typeof node === 'boolean') {
    return 'Empty';
  }
  if (typeof node === 'string' || typeof node === 'number') {
    return 'Text';
  }
  if (Array.isArray(node)) {
    return 'Fragment';
  }
  const { type } = node;
  if (typeof type === 'string') {
    return 'DOMElement';
  }
  return 'ComponentElement';
}

export function defaultTraverse(path) {
  const kind = kindOf(path.node);
  if (kind === 'Empty') {
    return path.node;
  }
  if (kind === 'Text') {
    return path.node;
  }
  if (kind === 'Fragment') {
    return path.node.map(path.traverse);
  }
  return React.cloneElement(
    path.node,
    path.node.props,
    ...path.traverseChildren(),
  );
}

export default function traverse(node, visitor) {
  const {
    Empty = defaultTraverse,
    Text = defaultTraverse,
    Fragment = defaultTraverse,
    DOMElement = defaultTraverse,
    ComponentElement = defaultTraverse
  } = visitor;
  const path = {
    node,
    kindOf,
    defaultTraverse() {
      return defaultTraverse(path);
    },
    traverse(childNode, childVisitor = visitor) {
      return traverse(childNode, childVisitor);
    },
    traverseChildren(childVisitor = visitor) {
      return React.Children.toArray(path.node.props.children).map(
        (childNode) => path.traverse(childNode, childVisitor)
      );
    },
    visitor
  };
  if (node === null || node === undefined || typeof node === 'boolean') {
    return Empty(path);
  }
  if (typeof node === 'string' || typeof node === 'number') {
    return Text(path);
  }
  if (Array.isArray(node)) {
    return Fragment(path);
  }
  const { type } = node;
  if (typeof type === 'string') {
    return DOMElement(path);
  }
  return ComponentElement(path);
}

// transpile.js
import React from 'react';
import { Image, Text, View } from 'react-native';

import traverse from './traverse';

const viewNodeTypes = ['div'];
const textNodeTypes = ['h1', 'p'];
const inlineNodeTypes = ['span', 'b', 'em'];

const dom2ReactNative = (node) => traverse(node, {
  ComponentElement(path) {
    if (path.node.type === 'img') {
      return React.createElement(
        Image,
        path.node.props
      );
    }
  },
  DOMElement(path) {
    if (textNodeTypes.includes(path.node.type)) {
      return React.createElement(
        Text,
        path.node.props,
        ...path.traverseChildren(),
      );
    } else if (viewNodeTypes.includes(path.node.type)) {
      return React.createElement(
        View,
        path.node.props,
        ...path.traverseChildren(),
      );
    } else if (inlineNodeTypes.includes(path.node.type)) {
      return path.node.props.children;
    }
    return React.cloneElement(
      path.node,
      path.node.props,
      ...path.traverseChildren(),
    );
  }
});

export default dom2ReactNative;