如何从外部更改 React 组件?

IT技术 reactjs
2021-05-13 05:54:46

可以认为是第三方组件的 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个回答

从使用 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 就像在这篇文章中一样

重构

一种干净的方法是修改组件以允许它txt-gray通过 prop有条件地添加

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

如果该组件由于属于第三方库而无法修改,则涉及派生库或用其修改后的副本替换第三方组件。

直接访问 DOM findDOMNode

一种解决方法是直接在父组件中访问 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', '')
      })
    ]
  });
}

这被认为是一种黑客行为,因为包装器组件应该知道修补的组件实现,如果实现更改,它可能会中断。

不,这是不可能的

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

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 或完全避免传递该props,它将使用灰色类。这是组件中的一个小变化,但它允许您不更改代码中使用此组件的所有点(并且您对灰色文本感到满意)