如何从外部更改React组件?

时间:2018-11-15 09:25:01

标签: reactjs

React组件(可以视为第三方组件)如下所示:

import * as React from 'react';
import classnames from 'classnames';
import { extractCommonClassNames } from '../../utils';

export const Tag = (props: React.ElementConfig): React$Node =>{
    const{
        classNames,
        props:
        {
            children,
            className,
            ...restProps
        },
    } = extractCommonClassNames(props);

    const combinedClassNames = classnames(
        'tag',
        className,
        ...classNames,
    );

    return (
        <span
          className={combinedClassNames}
          {...restProps}
        >
          {children}
          <i className="sbicon-times txt-gray" />
        </span>
    );
};

使用上面的组件的组件如下所示:

import React from 'react';
import * as L from '@korus/leda';
import type { KendoEvent } from '../../../types/general';

type Props = {
  visible: boolean
};

type State = {
  dropDownSelectData: Array<string>,
  dropDownSelectFilter: string
}

export class ApplicationSearch extends React.Component<Props, State> {
  constructor(props) {
    super(props);
    this.state = {
      dropDownSelectData: ['Имя', 'Фамилия', 'Машина'],
      dropDownSelectFilter: '',
    };
    this.onDropDownSelectFilterChange = this.onDropDownSelectFilterChange.bind(this);
  }

  componentDidMount() {
    document.querySelector('.sbicon-times.txt-gray').classList.remove('txt-gray');
  }

  onDropDownSelectFilterChange(event: KendoEvent) {
    const data = this.state.dropDownSelectData;
    const filter = event.filter.value;
    this.setState({
      dropDownSelectData: this.filterDropDownSelectData(data, filter),
      dropDownSelectFilter: filter,
    });
  }

  // eslint-disable-next-line class-methods-use-this
  filterDropDownSelectData(data, filter) {
    // eslint-disable-next-line func-names
    return data.filter(element => element.toLowerCase().indexOf(filter.toLowerCase()) > -1);
  }

  render() {
    const {
      visible,
    } = this.props;

    const {
      dropDownSelectData,
      dropDownSelectFilter,
    } = this.state;

    return (
      <React.Fragment>
        {
          visible && (
            <React.Fragment>
              <L.Block search active inner>
                <L.Block inner>
                  <L.Block tags>
                    <L.Tag>
                      option 1
                    </L.Tag>
                    <L.Tag>
                      option 2
                    </L.Tag>
                    <L.Tag>
                      ...
                    </L.Tag>
                  </L.Block>
                </L.Block>
              </React.Fragment>
            )}
      </React.Fragment>
    );
  }
}

是否可以从外部从组件中删除"txt-gray",如果可以,怎么办?

3 个答案:

答案 0 :(得分:1)

从使用Tag组件的位置删除该类:

componentDidMount() {
  document.querySelector('.sbicon-times.txt-gray').classList.remove('txt-gray')
}

或更具体地:

.querySelector('span i.sbicon-times.txt-gray')

根据您的评论,

  

我有多个带有“ txt-gray”的组件,但是当我使用您的代码时,“ txt-gray”仅从第一个组件中删除了。如何从所有组件中删除它?

我建议您使用代码删除使用Tag组件的父组件中的类。并且也使用querySelectorAll as in this post

答案 1 :(得分:1)

重构

一种干净的方法是修改组件,以使其可以通过prop有条件地添加txt-gray

<i className={classnames('sbicon-times', { 'txt-gray': props.gray })} />

如果由于组件属于第三方库而无法对其进行修改,则涉及到分叉库或将第三方组件替换为其修改后的副本。

使用findDOMNode

的直接DOM访问

一种解决方法是直接在父组件中访问DOM:

class TagWithoutGray extends React.Component {
  componentDidMount() {
    ReactDOM.findDOMNode(this).querySelector('i.sbicon-times.txt-gray')
    .classList.remove('txt-gray');
  }

  // unnecessary for this particular component
  componentDidUpdate = componentDidMount; 

  render() {
    return <Tag {...this.props}/>;
  }
}

通常不建议使用findDOMNode,因为直接DOM访问不是React惯用的,它存在性能问题并且与服务器端呈现不兼容。

使用cloneElement修补组件

另一个解决方法是修补组件。由于Tag是函数组件,因此可以直接调用它来访问和修改其子代:

const TagWithoutGray = props => {
  const span = Tag(props);
  const spanChildren = [...span.props.children];
  const i = spanChildren.pop();

  return React.cloneElement(span, {
    ...props,
    children: [
      ...spanChildren,
      React.cloneElement(i, {
        ...i.props,
        className: i.props.className.replace('txt-gray', '')
      })
    ]
  });
}

这被认为是黑客,因为包装器组件应了解修补的组件实现,如果实现发生更改,它可能会中断。

答案 2 :(得分:0)

否,不可能

唯一的方法是更改​​标签组件

import * as React from 'react';
import classnames from 'classnames';
import { extractCommonClassNames } from '../../utils';

export const Tag = (props: React.ElementConfig): React$Node =>{
    const{
        classNames,
        props:
        {
            children,
            className,
            ...restProps
        },
    } = extractCommonClassNames(props);

    const combinedClassNames = classnames(
        'tag',
        className,
        ...classNames,
    );

    const grayClass = this.props.disableGray ? 'sbicon-times' : 'sbicon-times txt-gray';

    return (
        <span
          className={combinedClassNames}
          {...restProps}
        >
          {children}
          <i className={grayClass} />
        </span>
    );
};

现在,如果您通过disableGray={true},它将取消灰度级;否则,如果您传递false或完全避免通过该道具,它将使用灰度级。这是该组件的一个很小的更改,但是它允许您不更改代码中使用该组件的所有点(您对灰色文本满意)