javascript - Vad är useState() i React?


Jag lär mig för närvarande konceptet med hooks i React och försöker förstå exemplet nedan.

import { useState } from 'react';

function Example() {
    // Declare a new state variable, which we'll call "count"
    const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

Exemplet ovan ökar räknaren på själva parameter för hanteringsfunktionen. Vad händer om jag vill ändra värdet på count inuti händelsehanteringsfunktionen

Tänk på exemplet nedan:

setCount = () => {
  //how can I modify count value here. Not sure if I can use setState to modify its value
  //also I want to modify other state values as well here. How can I do that
}

<button onClick={() => setCount()}>
  Click me
</button>

Svar #1

React hooks är ett nytt sätt (fortfarande under utveckling) att få tillgång till Reacts kärnfunktioner såsom state utan att behöva använda klasser. I ditt exempel, om du vill öka en räknare direkt i handlingsfunktionen utan att ange det direkt i onClick-egenskapen, kan du göra något liknande:

...
const [count, setCounter] = useState(0);
const [moreStuff, setMoreStuff] = useState(...);
...

const setCount = () => {
    setCounter(count + 1);
    setMoreStuff(...);
    ...
};

och onClick:

<button onClick={setCount}>
    Click me
</button>

Låt oss snabbt förklara vad som pågår i den här raden:

const [count, setCounter] = useState(0);

useState(0) returnerar en tupel där den första parametern count är det nuvarande värdet på räknaren och setCounter är metoden som gör att vi kan uppdatera räknarens tillstånd. Vi kan använda setCounter-metoden för att uppdatera tillståndet för count var som helst - i det här fallet använder vi den inne i setCount-funktionen där vi kan göra fler saker; idén med hooks är att vi kan hålla vår kod mer funktionell och undvika klassbaserade komponenter om det inte är önskat/behövs.

Jag skrev en komplett artikel om hooks med flera exempel (inklusive räknare) såsom denna codepen, jag använde useState, useEffect, useContext och anpassade hooks. Jag skulle kunna gå in mer i detalj om hur hooks fungerar i detta svar men dokumentationen gör ett mycket bra jobb med att förklara state-hooken och andra hooks i detalj.

uppdatering: Hooks är inte längre ett förslag, sedan version 16.8 är de nu tillgängliga att använda, det finns en sektion på Reacts sida som svarar på några av FAQ.

Svar #2

useState är en av de inbyggda React-hooks som finns tillgängliga i 0.16.7 versionen.

useState bör endast användas inuti funktionella komponenter. useState är sättet om vi behöver ett internt tillstånd och inte behöver implementera mer komplex logik såsom livscykelmetoder.

const [state, setState] = useState(initialState);

Returnerar ett tillståndsberoende värde och en funktion för att uppdatera det.

Under den initiala renderingen är det returnerade tillståndet (state) detsamma som värdet som skickades som det första argumentet (initialState).

Funktionen setState används för att uppdatera tillståndet. Den accepterar ett nytt tillståndsvärde och köar en återrendering av komponenten.

Observera att useState-hookens callback för att uppdatera tillståndet uppträder annorlunda än komponenternas this.setState. För att visa skillnaden har jag förberett två exempel.

class UserInfoClass extends React.Component {
  state = { firstName: 'John', lastName: 'Doe' };
  
  render() {
    return <div>
      <p>userInfo: {JSON.stringify(this.state)}</p>
      <button onClick={() => this.setState({ 
        firstName: 'Jason'
      })}>Update name to Jason</button>
    </div>;
  }
}

// Please note that new object is created when setUserInfo callback is used
function UserInfoFunction() {
  const [userInfo, setUserInfo] = React.useState({ 
    firstName: 'John', lastName: 'Doe',
  });

  return (
    <div>
      <p>userInfo: {JSON.stringify(userInfo)}</p>
      <button onClick={() => setUserInfo({ firstName: 'Jason' })}>Update name to Jason</button>
    </div>
  );
}

ReactDOM.render(
  <div>
    <UserInfoClass />
    <UserInfoFunction />
  </div>
, document.querySelector('#app'));
<script src="https://unpkg.com/[email /umd/react.development.js"></script>
<script src="https://unpkg.com/[email /umd/react-dom.development.js"></script>

<div id="app"></div>

Ett nytt objekt skapas när setUserInfo-callback används. Observera att vi förlorade värdet för lastName-nyckeln. För att åtgärda det kan vi skicka en funktion inuti useState.

setUserInfo(prevState => ({ ...prevState, firstName: 'Jason' })

Se exempel:

// Please note that new object is created when setUserInfo callback is used
function UserInfoFunction() {
  const [userInfo, setUserInfo] = React.useState({ 
    firstName: 'John', lastName: 'Doe',
  });

  return (
    <div>
      <p>userInfo: {JSON.stringify(userInfo)}</p>
      <button onClick={() => setUserInfo(prevState => ({
        ...prevState, firstName: 'Jason' }))}>
        Update name to Jason
      </button>
    </div>
  );
}

ReactDOM.render(
    <UserInfoFunction />
, document.querySelector('#app'));
<script src="https://unpkg.com/[email /umd/react.development.js"></script>
<script src="https://unpkg.com/[email /umd/react-dom.development.js"></script>

<div id="app"></div>

Till skillnad från setState-metoden som finns i klasskomponenter, slår useState inte automatiskt ihop uppdateringsobjekt. Du kan återskapa detta beteende genom att kombinera funktionsuppdateringsformen med objektets spridningssyntax:

setState(prevState => {
  // Object.assign would also work
  return {...prevState, ...updatedValues};
});

För mer om useState, se den officiella dokumentationen.

Svar #3

Syntaxen för useState-hooken är enkel.

const [value, setValue] = useState(defaultValue)

Om du inte är bekant med denna syntax, gå hit.

Jag skulle rekommendera att du läser dokumentationen. Det finns utmärkta förklaringar med en anständig mängd exempel.

import { useState } from 'react';

function Example() {
    // Declare a new state variable, which we'll call "count"
    const [count, setCount] = useState(0);
  
  // its up to you how you do it
  const buttonClickHandler = e => {
   // increment
   // setCount(count + 1)
   
   // decrement
   // setCount(count -1)
   
   // anything
   // setCount(0)
  }
  

  return (
       <div>
          <p>You clicked {count} times</p>
         <button onClick={buttonClickHandler}>
             Click me
         </button>
      </div>
   );
 }

Svar #4

useState() är en React-hook. Hooks gör det möjligt att använda state och muterbarhet inuti funktionskomponenter.

Även om du inte kan använda hooks inuti klasser kan du omsluta din klasskomponent med en funktionell komponent och använda hooks från den. Detta är ett fantastiskt verktyg för att migrera komponenter från klassform till funktionsform. Här är ett komplett exempel:

För detta exempel kommer jag att använda en räknarkomponent. Här är den:

class Hello extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: props.count };
  }
  
  inc() {
    this.setState(prev => ({count: prev.count+1}));
  }
  
  render() {
    return <button onClick={() => this.inc()}>{this.state.count}</button>
  }
}

ReactDOM.render(<Hello count={0}/>, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='root'></div>

Det är en enkel klasskomponent med ett räknartillstånd, och tillståndsuppdatering görs med hjälp av metoder. Detta är ett mycket vanligt mönster i klasskomponenter. Det första är att omsluta den med en funktionskomponent med samma namn, som delegerar alla sina egenskaper till den omslutna komponenten. Du behöver också rendera den omslutna komponenten i funktionsreturneringen. Här är det:

function Hello(props) {
  class Hello extends React.Component {
    constructor(props) {
      super(props);
      this.state = { count: props.count };
    }

    inc() {
      this.setState(prev => ({count: prev.count+1}));
    }

    render() {
      return <button onClick={() => this.inc()}>{this.state.count}</button>
    }
  }
  return <Hello {...props}/>
}

ReactDOM.render(<Hello count={0}/>, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='root'></div>

Detta är exakt samma komponent, med samma beteende, samma namn och samma egenskaper. Nu låt oss lyfta räknartillståndet till funktionskomponenten. Så här går det till:

function Hello(props) {
  const [count, setCount] = React.useState(0);
  class Hello extends React.Component {
    constructor(props) {
      super(props);
      this.state = { count: props.count };
    }

    inc() {
      this.setState(prev => ({count: prev.count+1}));
    }

    render() {
      return <button onClick={() => setCount(count+1)}>{count}</button>
    }
  }
  return <Hello {...props}/>
}

ReactDOM.render(<Hello count={0}/>, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js" integrity="sha256-3vo65ZXn5pfsCfGM5H55X+SmwJHBlyNHPwRmWAPgJnM=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js" integrity="sha256-qVsF1ftL3vUq8RFOLwPnKimXOLo72xguDliIxeffHRc=" crossorigin="anonymous"></script>
<div id='root'></div>

Observera att metoden inc fortfarande finns där, den skadar ingen, i själva verket är det död kod. Det här är idén, fortsätt bara att lyfta upp tillståndet. När du är klar kan du ta bort klasskomponenten:

function Hello(props) {
  const [count, setCount] = React.useState(0);

  return <button onClick={() => setCount(count+1)}>{count}</button>;
}

ReactDOM.render(<Hello count={0}/>, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js" integrity="sha256-3vo65ZXn5pfsCfGM5H55X+SmwJHBlyNHPwRmWAPgJnM=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js" integrity="sha256-qVsF1ftL3vUq8RFOLwPnKimXOLo72xguDliIxeffHRc=" crossorigin="anonymous"></script>

<div id='root'></div>

Även om detta gör det möjligt att använda hooks i klasskomponenter, skulle jag inte rekommendera dig att göra det, förutom om du migrerar som jag gjorde i detta exempel. Att blanda funktions- och klasskomponenter kommer att göra hanteringen av state rörig. Jag hoppas att detta hjälper

Vänliga hälsningar

Relaterade artiklar