Kun je een React-component dwingen opnieuw te renderen zonder setState aan te roepen?

Ik heb een extern (voor de component), waarneembaar object waarvan ik wil luisteren naar wijzigingen. Wanneer het object wordt bijgewerkt, worden er wijzigingsgebeurtenissen uitgezonden en vervolgens wil ik het onderdeel opnieuw weergeven wanneer er een wijziging wordt gedetecteerd.

Met een React.renderop het hoogste niveau was dit mogelijk, maar binnen een component werkt het niet (wat logisch is aangezien de methode rendergewoon terugkeert een object).

Hier is een codevoorbeeld:

export default class MyComponent extends React.Component {
  handleButtonClick() {
    this.render();
  }
  render() {
    return (
      <div>
        {Math.random()}
        <button onClick={this.handleButtonClick.bind(this)}>
          Click me
        </button>
      </div>
    )
  }
}

Als u intern op de knop klikt, wordt this.render()aangeroepen, maar dat is niet de oorzaak van het renderen (u kunt dit in actie zien omdat de tekst gemaakt door {Math.random()}verandert niet). Als ik echter gewoon this.setState()aanroep in plaats van this.render(), werkt het prima.

Dus ik denk dat mijn vraag is:moeten React-componenten moeteneen status hebben om opnieuw te kunnen renderen? Is er een manier om het onderdeel te dwingen om op aanvraag te updaten zonder de status te wijzigen?


Antwoord 1, autoriteit 100%

In klassecomponenten kun je this.forceUpdate()aanroepen om een re-render te forceren.

Documentatie: https://facebook.github.io/react/ docs/component-api.html

In functiecomponenten is er geen equivalent van forceUpdate, maar u kunt een manier om updates mee te dwingen De useStateHook .


Antwoord 2, Autoriteit 43%

forceUpdatemoet worden vermeden omdat het afwijkt van een reageermindset. De React Documenten noemen een voorbeeld van wanneer forceUpdatekan worden gebruikt:

Standaard, wanneer de status of rekwisieten van uw component verandert, wordt uw component opnieuw weergegeven. Als deze echter impliciet verandert (bijv.: Gegevens die diep in een object worden gewijzigd zonder het object zelf te wijzigen) of als de methode van uw render () afhankelijk is van sommige andere gegevens, kunt u zien dat het opnieuw moet worden uitgevoerd () door te roepen () krachtupdate ().

Ik zou echter het idee willen voorstellen dat zelfs met diep geneste objecten, forceUpdateonnodig is. Door gebruik te maken van een onveranderlijke gegevensbron wordt het trackingwijzigingen goedkoop; Een wijziging zal altijd resulteren in een nieuw object, dus we hoeven alleen maar te controleren of de verwijzing naar het object is gewijzigd. U kunt de bibliotheek onveranderlijk JS gebruiken om onveranderlijke gegevensobjecten in uw app te implementeren.

Normaal gesproken moet u proberen om alle gebruikswijzen van TOWNUPDATE () te vermijden en alleen te lezen van deze. Props en dit.State op het renderen (). Dit maakt uw component “PURE” en uw applicatie veel eenvoudiger en efficiënter. TOWERUPDATE ()

De sleutel van het element wijzigen dat u opnieuw wilt worden weergegeven, werkt. Stel de toets Prop op uw element in via de staat en wanneer u insteltoestand wilt bijwerken om een ​​nieuwe sleutel te hebben.

<Element key={this.state.key} /> 

Vervolgens vindt er een wijziging plaats en reset u de sleutel

this.setState({ key: Math.random() });

Ik wil opmerken dat dit het element zal vervangen waarop de sleutel wordt gewijzigd. Een voorbeeld van waar dit handig kan zijn, is wanneer u een invoerveld voor bestanden heeft dat u wilt resetten na het uploaden van een afbeelding.

Hoewel het echte antwoord op de vraag van de OP forceUpdate()zou zijn, vond ik deze oplossing nuttig in verschillende situaties. Ik wil er ook op wijzen dat als je merkt dat je forceUpdategebruikt, je misschien je code wilt bekijken en kijken of er een andere manier is om dingen te doen.

OPMERKING 1-9-2019:

Het bovenstaande (de sleutel wijzigen) zal het element volledig vervangen. Als u merkt dat u de sleutel bijwerkt om wijzigingen door te voeren, heeft u waarschijnlijk ergens anders een probleem in uw code. Door Math.random()in key te gebruiken, wordt het element bij elke render opnieuw gemaakt. Ik zou NIET aanraden om de sleutel op deze manier bij te werken, aangezien react de sleutel gebruikt om te bepalen wat de beste manier is om dingen opnieuw te renderen.


Antwoord 3, autoriteit 12%

Eigenlijk is forceUpdate()de enige juiste oplossing aangezien setState()nieteen re-render kan activeren als aanvullende logica wordt geïmplementeerd in shouldComponentUpdate()of wanneer het gewoon falseretourneert.

forceUpdate()

Als u forceUpdate()aanroept, wordt render()aangeroepen op de component, waarbij shouldComponentUpdate()wordt overgeslagen. meer…

SETSTATE ()

setState()zal altijd een re-render activeren tenzij voorwaardelijke renderinglogica wordt geïmplementeerd in shouldComponentUpdate(). Meer …


forceUpdate()Kan vanuit uw component worden opgeroepen door this.forceUpdate()


Haken: Hoe kan ik component dwingen om opnieuw te renderen met haken in reageer?


BTW: Muteer je staat of je geneste eigenschappen niet verspreiden?


Antwoord 4, Autoriteit 5%

Ik vermeed forceUpdatedoor het volgende te doen

verkeerde manier : Gebruik geen index als sleutel

this.state.rows.map((item, index) =>
   <MyComponent cell={item} key={index} />
)

Correcte manier : Gebruik de data-ID als sleutel, het kan enkele GUID zijn enz.

this.state.rows.map((item) =>
   <MyComponent item={item} key={item.id} />
)

Dus door dergelijke code te doen, zal uw component uniek zijn en natuurlijk

renderen


Antwoord 5, Autoriteit 4%

Als je twee React-componenten wilt laten communiceren, die niet aan een relatie (ouder-kind) zijn gebonden, is het raadzaam om Fluxof vergelijkbare architecturen.

Wat u wilt doen, is luisteren naar wijzigingen van de waarneembare component-winkel, die het model en zijn interface bevat, en de gegevens opslaan die ervoor zorgen dat de weergave verandert als statein MyComponent. Wanneer de winkel de nieuwe gegevens pusht, wijzigt u de status van uw component, waardoor de weergave automatisch wordt geactiveerd.

Normaal gesproken moet u proberen het gebruik van forceUpdate()te vermijden. Uit de documentatie:

Normaal gesproken zou je moeten proberen alle gebruik van forceUpdate() te vermijden en alleen te lezen van this.props en this.state in render(). Dit maakt uw aanvraag veel eenvoudiger en efficiënter


Antwoord 6, autoriteit 2%

gebruik haken of HOC maak je keuze

Met behulp van hakenof het HOC (higher order component) patroon, kunt u automatische updates krijgen wanneer uw winkels veranderen. Dit is een zeer lichtgewicht aanpak zonder kader.

useStoreHakt manier om winkelupdates af te handelen

interface ISimpleStore {
  on: (ev: string, fn: () => void) => void;
  off: (ev: string, fn: () => void) => void;
}
export default function useStore<T extends ISimpleStore>(store: T) {
  const [storeState, setStoreState] = useState({store});
  useEffect(() => {
    const onChange = () => {
      setStoreState({store});
    }
    store.on('change', onChange);
    return () => {
      store.off('change', onChange);
    }
  }, []);
  return storeState.store;
}

withStoresHOC zorgt voor winkelupdates

export default function (...stores: SimpleStore[]) {
  return function (WrappedComponent: React.ComponentType<any>) {
    return class WithStore extends PureComponent<{}, {lastUpdated: number}> {
      constructor(props: React.ComponentProps<any>) {
        super(props);
        this.state = {
          lastUpdated: Date.now(),
        };
        this.stores = stores;
      }
      private stores?: SimpleStore[];
      private onChange = () => {
        this.setState({lastUpdated: Date.now()});
      };
      componentDidMount = () => {
        this.stores &&
          this.stores.forEach((store) => {
            // each store has a common change event to subscribe to
            store.on('change', this.onChange);
          });
      };
      componentWillUnmount = () => {
        this.stores &&
          this.stores.forEach((store) => {
            store.off('change', this.onChange);
          });
      };
      render() {
        return (
          <WrappedComponent
            lastUpdated={this.state.lastUpdated}
            {...this.props}
          />
        );
      }
    };
  };
}

SimpleStore-klasse

import AsyncStorage from '@react-native-community/async-storage';
import ee, {Emitter} from 'event-emitter';
interface SimpleStoreArgs {
  key?: string;
  defaultState?: {[key: string]: any};
}
export default class SimpleStore {
  constructor({key, defaultState}: SimpleStoreArgs) {
    if (key) {
      this.key = key;
      // hydrate here if you want w/ localState or AsyncStorage
    }
    if (defaultState) {
      this._state = {...defaultState, loaded: false};
    } else {
      this._state = {loaded: true};
    }
  }
  protected key: string = '';
  protected _state: {[key: string]: any} = {};
  protected eventEmitter: Emitter = ee({});
  public setState(newState: {[key: string]: any}) {
    this._state = {...this._state, ...newState};
    this.eventEmitter.emit('change');
    if (this.key) {
      // store on client w/ localState or AsyncStorage
    }
  }
  public get state() {
    return this._state;
  }
  public on(ev: string, fn:() => void) {
    this.eventEmitter.on(ev, fn);
  }
  public off(ev: string, fn:() => void) {
    this.eventEmitter.off(ev, fn);
  }
  public get loaded(): boolean {
    return !!this._state.loaded;
  }
}

Hoe te gebruiken

In het geval van haken:

// use inside function like so
const someState = useStore(myStore);
someState.myProp = 'something';

In het geval van HOC:

// inside your code get/set your store and stuff just updates
const val = myStore.myProp;
myOtherStore.myProp = 'something';
// return your wrapped component like so
export default withStores(myStore)(MyComponent);

ZORG ERVOOR
Om uw winkels als singleton te exporteren om zo te profiteren van wereldwijde verandering:

class MyStore extends SimpleStore {
  public get someProp() {
    return this._state.someProp || '';
  }
  public set someProp(value: string) {
    this.setState({...this._state, someProp: value});
  }
}
// this is a singleton
const myStore = new MyStore();
export {myStore};

Deze aanpak is vrij eenvoudig en werkt voor mij. Ik werk ook in grote teams en gebruik Redux en MobX en vind die ook goed, maar gewoon veel boilerplate. Ik hou persoonlijk gewoon van mijn eigen aanpak omdat ik altijd een hekel heb gehad aan veel code voor iets dat eenvoudig kan zijn als je het nodig hebt.


Antwoord 7, autoriteit 2%

In 2021 is dit de officiële manierom een React Functional Component te forceren.

const [, forceUpdate] = useReducer(x => x + 1, 0);
  function handleClick() {
    forceUpdate();
  }

Ik weet dat de OP voor een klassencomponent is. Maar de vraag werd in 2015 gesteld en nu hooks beschikbaar zijn, kunnen velen zoeken naar forceUpdatein functionele componenten. Dit kleine beetje is voor hen.


Antwoord 8, autoriteit 2%

Dus ik denk dat mijn vraag is: moeten React-componenten de status in hebben?
opnieuw weergeven? Is er een manier om het onderdeel te forceren om te updaten?
vraag zonder de staat te veranderen?

De andere antwoorden hebben geprobeerd te illustreren hoe je dat zou kunnen, maar het punt is dat je dat niet zou moeten. Zelfs de hacky oplossing van het veranderen van de sleutel mist het punt. De kracht van React is het opgeven van de controle over het handmatig beheren wanneer iets moet worden weergegeven, en in plaats daarvan je alleen bezig te houden met hoe iets op invoer moet worden weergegeven. Geef vervolgens een stroom van inputs.

Als je handmatig opnieuw renderen moet forceren, doe je vrijwel zeker iets niet goed.


Antwoord 9

Er zijn een paar manieren om uw component opnieuw te renderen:

De eenvoudigste oplossing is om de forceUpdate()-methode te gebruiken:

this.forceUpdate()

Nog een oplossing is om een niet-gebruikte sleutel te maken in de status (niet-gebruikte sleutel)
en roep de setState-functie aan met update van deze niet-gebruikte sleutel:

this.setState({ nonUsedKey: Date.now() } );

Of herschrijf alle huidige status:

this.setState(this.state);

Het wisselen van rekwisieten zorgt ook voor opnieuw renderen van componenten.


Antwoord 10

Voor de volledigheid kun je dit ook bereiken in functionele componenten:

const [, updateState] = useState();
const forceUpdate = useCallback(() => updateState({}), []);
// ...
forceUpdate();

Of, als herbruikbare haak:

const useForceUpdate = () => {
  const [, updateState] = useState();
  return useCallback(() => updateState({}), []);
}
// const forceUpdate = useForceUpdate();

Zie: https://stackoverflow.com/a/53215514/2692307

Houd er rekening mee dat het gebruik van een force-update-mechanisme nog steeds een slechte gewoonte is, omdat het indruist tegen de reactie-mentaliteit, dus het moet nog steeds worden vermeden indien mogelijk.


Antwoord 11

Je zou het op een aantal manieren kunnen doen:

1. Gebruik de forceUpdate()methode:

Er zijn enkele problemen die kunnen optreden bij het gebruik van de forceUpdate()-methode. Een voorbeeld is dat het de methode shouldComponentUpdate()negeert en de weergave opnieuw rendert, ongeacht of shouldComponentUpdate()false retourneert. Daarom moet het gebruik van forceUpdate() zoveel mogelijk worden vermeden.

2. This.state doorgeven aan de setState()methode

De volgende regel code lost het probleem met het vorige voorbeeld op:

this.setState(this.state);

Het enige wat dit doet is de huidige status overschrijven met de huidige status, wat een re-rendering activeert. Dit is nog steeds niet per se de beste manier om dingen te doen, maar het verhelpt wel enkele van de problemen die u kunt tegenkomen bij het gebruik van de forceUpdate()-methode.


Antwoord 12

We kunnen this.forceUpdate() gebruiken zoals hieronder.

      class MyComponent extends React.Component {
      handleButtonClick = ()=>{
          this.forceUpdate();
     }
 render() {
   return (
     <div>
      {Math.random()}
        <button  onClick={this.handleButtonClick}>
        Click me
        </button>
     </div>
    )
  }
}
 ReactDOM.render(<MyComponent /> , mountNode);

Het element ‘Math.random’ in de DOM wordt alleen bijgewerkt, zelfs als u de setState gebruikt om de component opnieuw te renderen.

Alle antwoorden hier zijn correct en vormen een aanvulling op de vraag voor begrip..Omdat we weten dat het opnieuw renderen van een component zonder setState({}) is door de forceUpdate() te gebruiken.

De bovenstaande code wordt uitgevoerd met setState zoals hieronder.

class MyComponent extends React.Component {
             handleButtonClick = ()=>{
                this.setState({ });
              }
        render() {
         return (
  <div>
    {Math.random()}
    <button  onClick={this.handleButtonClick}>
      Click me
    </button>
  </div>
)
  }
 }
ReactDOM.render(<MyComponent /> , mountNode);

Antwoord 13

Nog een antwoord om het geaccepteerde antwoord te ondersteunen 🙂

React ontmoedigt het gebruik van forceUpdate()omdat ze over het algemeen een zeer “dit is de enige manier om het te doen” benadering hebben voor functioneel programmeren. In veel gevallen is dit prima, maar veel React-ontwikkelaars hebben een OO-achtergrond en met die benadering is het prima om naar een waarneembaar object te luisteren.

En als je dat doet, weet je waarschijnlijk dat je opnieuw MOET renderen wanneer het waarneembare “vuurt”, en daarom MOET je forceUpdate()gebruiken en het is eigenlijk een pluspunt dat shouldComponentUpdate()is hier NIET bij betrokken.

Tools zoals MobX, die een OO-benadering hebben, doen dit eigenlijk onder de oppervlakte (eigenlijk roept MobX render()rechtstreeks aan)


Antwoord 14

Ik vond het het beste om forceUpdate() te vermijden. Een manier om opnieuw renderen te forceren is om de afhankelijkheid van render() toe te voegen aan een tijdelijke externe variabele en de waarde van die variabele te wijzigen als en wanneer dat nodig is.

Hier is een codevoorbeeld:

class Example extends Component{
   constructor(props){
      this.state = {temp:0};
      this.forceChange = this.forceChange.bind(this);
   }
   forceChange(){
      this.setState(prevState => ({
          temp: prevState.temp++
      })); 
   }
   render(){
      return(
         <div>{this.state.temp &&
             <div>
                  ... add code here ...
             </div>}
         </div>
      )
   }
}

Noem this.forceChange() wanneer u opnieuw renderen moet forceren.


Antwoord 15

ES6 – Ik voeg een voorbeeld toe, wat nuttig voor mij was:

In een “korte if-statement” kun je een lege functie als volgt doorgeven:

isReady ? ()=>{} : onClick

Dit lijkt de kortste benadering te zijn.

()=>{}

Antwoord 16

forceUpdate();methode zal werken, maar het is aan te raden om setState();

te gebruiken


Antwoord 17

Probeer this.forceUpdate() om te bereiken wat u beschrijft.


Antwoord 18

Een andere manier is om setState, ENstatus te behouden:

this.setState(prevState=>({...prevState}));


Antwoord 19

forceUpdate(), maar elke keer als ik er ooit iemand over heb horen praten, is er contact met je opgenomen dat je dit nooit moet gebruiken.


Antwoord 20

Je kunt forceUpdate() gebruiken voor meer details check (forceUpdate()).

Other episodes