È antipattern da usare React.cloneElement per estendere un elemento?

1

Sto creando un componente popover per un'interfaccia utente in React. Quel componente contiene un pulsante che attiva la visualizzazione del popover. Perché il pulsante deve essere configurabile - etichetta, classi, proprietà - Ho bisogno di trasmettere questi parametri di configurazione al bambino. Ci sono due modi principali in cui vedo questo accadere.

  1. Abbassa etichetta e oggetti di scena come attributi:
<PopoverComponent buttonLabel={label} buttonProps={buttonProps} />
  1. Passa al pulsante attuale.
const button = <Button>Show</Button>;

<PopoverComponent button={button} />

La complicazione arriva all'interno del popover. Per posizionare l'elemento ho bisogno di un ref sul nodo DOM, quindi devo aggiungere oggetti di scena all'elemento button all'interno di PopoverComponent . In React questo è semplice e standard nel caso 1. Basta spargere puntelli personalizzati sul pulsante. Ad esempio, <Button ref="popoverButton" onClick={this.onClick} {...extraProps }> . Tuttavia, nel caso 2, dobbiamo React.cloneElement e mutare gli oggetti di scena. Per es.,

// INSIDE popoverComponent

const { button } = this.props;

const extendedButton = React.cloneElement(
  button,
  Object.assign(
    {},
    button.props, {
      ref: "popoverButton",
      onClick: this.onClick,
    }),
);

return <div className="popover-control">{ extendedButton }</div>;

È antipattern usare React.cloneElement e modificare oggetti di scena in componenti figlio in React?

    
posta James 03.03.2018 - 20:42
fonte

2 risposte

2

Non sono arrivato alla conclusione se è anti-modello o no, ma ci sono pro e contro per ogni approccio. Nella mia implementazione effettiva supporto entrambe le interfacce per migliorare l'usabilità degli sviluppatori a valle. Lo sviluppatore può fornire un vero elemento React per il controllo completo o oggetti di scena per il comportamento predefinito.

Utilizzo di cloneElement

Pro

  • Dai al consumatore il pieno controllo dell'istanza del pulsante, ad eccezione degli oggetti di scena che devi accedere / aggiungere / modificare.

Contro

  • Devi sapere esattamente cosa sovrascrivere (diventerà non mantenibile sugli elementi nidificati). Quindi essenzialmente è utile solo per le modifiche superficiali.
  • Più difficile estendere / modificare l'elemento a valle (punto precedente).

Passaggio di props

Pro

  • Molto più facile estendere l'elemento tramite JSX.
  • L'estensione segue il modello tradizionale di genitore / figlio in React.

Contro

  • Meno controllo per il consumatore. Possono avere componenti di pulsanti specifici sulla loro pagina e all'improvviso possono solo passare in oggetti di scena / etichetta per controllare questo componente.
risposta data 06.03.2018 - 19:09
fonte
3

L'aggiunta di oggetti di scena aggiuntivi a un componente è lo scopo di React.cloneElement :

Clone and return a new React element using element as the starting point. The resulting element will have the original element’s props with the new props merged in shallowly.

Di solito lo uso in combinazione con altri metodi da API React.Children per incapsulare determinate funzionalità in un componente contenitore genitore. Ad esempio, un contenitore di gestione stato selezionato per una sequenza di un numero qualsiasi di caselle di controllo:

class CheckboxContainer extends React.Component {
  constructor (props) {
    super();
    this.onClick = this.onClick.bind(this);
    this.state = {
      checked: Array(React.Children.count(props.children)).fill(false)
    };
  }

  onClick (index) {
    this.setState({
      checked: Object.assign([], this.state.checked, {
        index: !this.state.checked[index]
      })
    });
  }

  render () {
    return React.Children.map(this.props.children, (child, index) => {
      return React.cloneElement(child, {
        index: index,
        onClick: () => this.onClick(index),
        checked: this.state.checked[index]
      });
    });
  }
}

class Checkbox extends React.Component {
  render () {
    return (
      <div>
        <input type='checkbox'
          id={'checkbox-${this.props.index}'}
          value={this.props.checked} />
        <label for={'checkbox-${this.props.index}'}>
          {'Child ${this.props.index}'}
        </label>
      </div>
    );
  }
}

class Test extends React.Component {
  render () {
    return (
      <CheckboxContainer>
        <Checkbox />
        <Checkbox />
        <Checkbox />
      </CheckboxContainer>
    );
  }
}

Potresti fare qualcosa di simile. Chiedi a tuo padre PopoverComponent di gestire lo stato aperto e utilizzare il componente Button come figlio. Qualunque oggetto di gioco si riferisce solo al pulsante dichiarare sul pulsante e unire eventuali oggetti di scena relativi alla gestione dello stato di popover quando lo si clona nel genitore:

<PopoverComponent>
  <Button label='label' />
</PopoverComponent>

Tuttavia, penso che i popover siano casi speciali perché dovrebbero apparire come elementi di rendering in cima agli elementi HTML sottostanti, quindi non è una chiara relazione genitore / figlio. Nota anche key e ref sono trattati in modo diverso rispetto ad altri oggetti di scena quando si utilizza React.cloneElement :

key and ref from the original element will be preserved.

    
risposta data 12.05.2018 - 19:54
fonte

Leggi altre domande sui tag