如何使用useState钩子同时设置多个状态?

时间:2020-11-12 14:32:14

标签: javascript reactjs

我想合并多个状态并使用useState钩子同时处理它们,请检查以下示例,该示例更新了用户输入上的一些文本:

const {useState} = React;

const Example = ({title}) => {
  const initialState = {
    name: 'John',
    age: 25
  };
  const [{name, age}, setFormState] = useState(initialState);
  const handleNameChange = (e) => {
    setFormState({
      name: e.target.value,
      age
    });
  };
  const handleAgeChange = (e) => {
    setFormState({
      name,
      age: e.target.value
    })
  };
  return (
    <form onSubmit={e=>e.preventDefault()}>
      <input type='text' id='name' name='name' placeholder={name} onChange={handleNameChange} />
                <p>The person's name is {name}.</p>
                <br />
                <label htmlFor='age'>Age: </label>
                <input type='text' id='age' name='age' placeholder={age} onChange={handleAgeChange} />
                <p>His/her age is {age}.</p>
    </form>
  );
};

// Render it
ReactDOM.render(
  <Example />,
  document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>

代码运行良好,但是如您所见,我正在使用2个函数分别处理名称和年龄。这与我保存一些代码的意图背道而驰。是否可以仅使用1个功能分别更改姓名和年龄?对此进行了尝试,但显然它将使用相同的值进行更新。

const {useState} = React;

const Example = ({title}) => {
  const initialState = {
    name: 'John',
    age: 25
  };
  const [{name, age}, setFormState] = useState(initialState);
  const handleChange = (e) => {
    setFormState({
      name: e.target.value,
      age: e.target.value
    });
  };
  return (
    <form onSubmit={e=>e.preventDefault()}>
      <input type='text' id='name' name='name' placeholder={name} onChange={handleChange} />
                <p>The person's name is {name}.</p>
                <br />
                <label htmlFor='age'>Age: </label>
                <input type='text' id='age' name='age' placeholder={age} onChange={handleChange} />
                <p>His/her age is {age}.</p>
    </form>
  );
};

// Render it
ReactDOM.render(
  <Example />,
  document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>

2 个答案:

答案 0 :(得分:2)

使用单个useState钩子无法同时设置多个不同的状态。您可以分别设置它们, 例如:

const [userName, setUserName] = useState('');
const [password, setPassword] = useState('');

或将所有状态放入对象,然后使用useState挂钩更新该对象。 来源:https://daveceddia.com/usestate-hook-examples/ 示例:

const [form, setState] = useState({
  username: '',
  password: ''
});

const updateField = e => {
    setState({
      ...form,
      [e.target.name]: e.target.value
    });
  };

return (
    <form onSubmit={printValues}>
      <label>
        Username:
        <input
          value={form.username}
          name="username"
          onChange={updateField}
        />
      </label>
      <br />
      <label>
        Password:
        <input
          value={form.password}
          name="password"
          type="password"
          onChange={updateField}
        />
      </label>
      <br />
      <button>Submit</button>
    </form>
  );

答案 1 :(得分:0)

您有两种选择可以达到预期的效果。

例如,您可以创建一个函数工厂。

ERROR org.springframework.boot.SpringApplication - Application run failed
cdb-be_1  | java.lang.IllegalStateException: Failed to configure KeyVault property source
cdb-be_1  |     at com.microsoft.azure.keyvault.spring.KeyVaultEnvironmentPostProcessorHelper.addKeyVaultPropertySource(KeyVaultEnvironmentPostProcessorHelper.java:80)
cdb-be_1  |     at com.microsoft.azure.keyvault.spring.KeyVaultEnvironmentPostProcessor.postProcessEnvironment(KeyVaultEnvironmentPostProcessor.java:26)
cdb-be_1  |     at org.springframework.boot.context.config.ConfigFileApplicationListener.onApplicationEnvironmentPreparedEvent(ConfigFileApplicationListener.java:177)
cdb-be_1  |     at org.springframework.boot.context.config.ConfigFileApplicationListener.onApplicationEvent(ConfigFileApplicationListener.java:165)
cdb-be_1  |     at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
cdb-be_1  |     at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
cdb-be_1  |     at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
cdb-be_1  |     at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127)
cdb-be_1  |     at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:76)
cdb-be_1  |     at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:53)
cdb-be_1  |     at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:342)
cdb-be_1  |     at org.springframework.boot.SpringApplication.run(SpringApplication.java:305)
cdb-be_1  |     at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215)
cdb-be_1  |     at org.springframework.boot.SpringApplication.run(SpringApplication.java:1204)
cdb-be_1  |     at com.siemens.sfs.cdb.be.CdbBackendApplication.main(CdbBackendApplication.java:71)
cdb-be_1  |     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
cdb-be_1  |     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
cdb-be_1  |     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
cdb-be_1  |     at java.lang.reflect.Method.invoke(Method.java:498)
cdb-be_1  |     at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48)
cdb-be_1  |     at org.springframework.boot.loader.Launcher.launch(Launcher.java:87)
cdb-be_1  |     at org.springframework.boot.loader.Launcher.launch(Launcher.java:51)
cdb-be_1  |     at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:52)
cdb-be_1  | Caused by: java.lang.IllegalStateException: Failed to do authentication.
cdb-be_1  |     at com.microsoft.azure.keyvault.spring.AzureKeyVaultCredential.refreshToken(AzureKeyVaultCredential.java:66)
cdb-be_1  |     at com.microsoft.azure.keyvault.spring.AzureKeyVaultCredential.doAuthenticate(AzureKeyVaultCredential.java:46)
cdb-be_1  |     at com.microsoft.azure.keyvault.authentication.KeyVaultCredentials.doAuthenticate(KeyVaultCredentials.java:420)
cdb-be_1  |     at com.microsoft.azure.keyvault.authentication.KeyVaultCredentials.getAuthenticationCredentials(KeyVaultCredentials.java:224)
cdb-be_1  |     at com.microsoft.azure.keyvault.authentication.KeyVaultCredentials.buildAuthenticatedRequest(KeyVaultCredentials.java:123)
cdb-be_1  |     at com.microsoft.azure.keyvault.authentication.KeyVaultCredentials.buildAuthenticatedRequest(KeyVaultCredentials.java:161)
cdb-be_1  |     at com.microsoft.azure.keyvault.authentication.KeyVaultCredentials.access$300(KeyVaultCredentials.java:34)
cdb-be_1  |     at com.microsoft.azure.keyvault.authentication.KeyVaultCredentials$1.intercept(KeyVaultCredentials.java:76)
cdb-be_1  |     at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
cdb-be_1  |     at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
cdb-be_1  |     at com.microsoft.rest.interceptors.BaseUrlHandler.intercept(BaseUrlHandler.java:43)
cdb-be_1  |     at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
cdb-be_1  |     at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
cdb-be_1  |     at com.microsoft.rest.interceptors.RequestIdHeaderInterceptor.intercept(RequestIdHeaderInterceptor.java:29)
cdb-be_1  |     at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
cdb-be_1  |     at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
cdb-be_1  |     at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:257)
cdb-be_1  |     at okhttp3.RealCall.execute(RealCall.java:93)
cdb-be_1  |     at retrofit2.OkHttpCall.execute(OkHttpCall.java:186)
cdb-be_1  |     at retrofit2.adapter.rxjava.CallExecuteOnSubscribe.call(CallExecuteOnSubscribe.java:40)
cdb-be_1  |     at retrofit2.adapter.rxjava.CallExecuteOnSubscribe.call(CallExecuteOnSubscribe.java:24)
cdb-be_1  |     at rx.Observable.unsafeSubscribe(Observable.java:10327)
cdb-be_1  |     at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:48)
cdb-be_1  |     at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:33)
cdb-be_1  |     at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
cdb-be_1  |     at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
cdb-be_1  |     at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
cdb-be_1  |     at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
cdb-be_1  |     at rx.Observable.subscribe(Observable.java:10423)
cdb-be_1  |     at rx.Observable.subscribe(Observable.java:10390)
cdb-be_1  |     at rx.observables.BlockingObservable.blockForSingle(BlockingObservable.java:443)
cdb-be_1  |     at rx.observables.BlockingObservable.single(BlockingObservable.java:340)
cdb-be_1  |     at com.microsoft.azure.keyvault.implementation.KeyVaultClientBaseImpl.getSecrets(KeyVaultClientBaseImpl.java:3922)
cdb-be_1  |     at com.microsoft.azure.keyvault.implementation.KeyVaultClientCustomImpl.listSecrets(KeyVaultClientCustomImpl.java:1170)
cdb-be_1  |     at com.microsoft.azure.keyvault.spring.KeyVaultOperation.fillSecretsList(KeyVaultOperation.java:138)
cdb-be_1  |     at com.microsoft.azure.keyvault.spring.KeyVaultOperation.<init>(KeyVaultOperation.java:48)
cdb-be_1  |     at com.microsoft.azure.keyvault.spring.KeyVaultEnvironmentPostProcessorHelper.addKeyVaultPropertySource(KeyVaultEnvironmentPostProcessorHelper.java:69)
cdb-be_1  |     ... 22 common frames omitted
cdb-be_1  | Caused by: java.lang.IllegalStateException: Failed to do authentication.
cdb-be_1  |     at com.microsoft.azure.utils.AADAuthUtil.getToken(AADAuthUtil.java:31)
cdb-be_1  |     at com.microsoft.azure.keyvault.spring.AzureKeyVaultCredential.refreshToken(AzureKeyVaultCredential.java:57)
cdb-be_1  |     ... 58 common frames omitted
cdb-be_1  | Caused by: java.util.concurrent.ExecutionException: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
cdb-be_1  |     at java.util.concurrent.FutureTask.report(FutureTask.java:122)
cdb-be_1  |     at java.util.concurrent.FutureTask.get(FutureTask.java:206)
cdb-be_1  |     at com.microsoft.azure.utils.AADAuthUtil.getToken(AADAuthUtil.java:29)
cdb-be_1  |     ... 59 common frames omitted
cdb-be_1  | Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
cdb-be_1  |     at sun.security.ssl.Alerts.getSSLException(Alerts.java:198)
cdb-be_1  |     at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1967)
cdb-be_1  |     at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:331)
cdb-be_1  |     at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:325)
cdb-be_1  |     at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1688)
cdb-be_1  |     at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:226)
cdb-be_1  |     at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1082)
cdb-be_1  |     at sun.security.ssl.Handshaker.process_record(Handshaker.java:1010)
cdb-be_1  |     at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1079)
cdb-be_1  |     at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1388)
cdb-be_1  |     at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1416)
cdb-be_1  |     at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1400)
cdb-be_1  |     at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559)
cdb-be_1  |     at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
cdb-be_1  |     at sun.net.www.protocol.http.HttpURLConnection.getOutputStream0(HttpURLConnection.java:1340)
cdb-be_1  |     at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:1315)
cdb-be_1  |     at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:264)
cdb-be_1  |     at com.microsoft.aad.adal4j.AdalOAuthRequest.configureHeaderAndExecuteOAuthCall(AdalOAuthRequest.java:145)
cdb-be_1  |     at com.microsoft.aad.adal4j.AdalOAuthRequest.send(AdalOAuthRequest.java:83)
cdb-be_1  |     at com.microsoft.aad.adal4j.AdalTokenRequest.executeOAuthRequestAndProcessResponse(AdalTokenRequest.java:87)
cdb-be_1  |     at com.microsoft.aad.adal4j.AuthenticationContext.acquireTokenCommon(AuthenticationContext.java:930)
cdb-be_1  |     at com.microsoft.aad.adal4j.AcquireTokenCallable.execute(AcquireTokenCallable.java:70)
cdb-be_1  |     at com.microsoft.aad.adal4j.AcquireTokenCallable.execute(AcquireTokenCallable.java:38)
cdb-be_1  |     at com.microsoft.aad.adal4j.AdalCallable.call(AdalCallable.java:47)
cdb-be_1  |     at java.util.concurrent.FutureTask.run(FutureTask.java:266)
cdb-be_1  |     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
cdb-be_1  |     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
cdb-be_1  |     at java.lang.Thread.run(Thread.java:748)
cdb-be_1  | Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
cdb-be_1  |     at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:450)
cdb-be_1  |     at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:317)
cdb-be_1  |     at sun.security.validator.Validator.validate(Validator.java:262)
cdb-be_1  |     at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:330)
cdb-be_1  |     at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:237)
cdb-be_1  |     at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:132)
cdb-be_1  |     at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1670)
cdb-be_1  |     ... 23 common frames omitted
cdb-be_1  | Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
cdb-be_1  |     at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141)
cdb-be_1  |     at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126)
cdb-be_1  |     at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
cdb-be_1  |     at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:445)
cdb-be_1  |     ... 29 common frames omitted
const {useState} = React;

const Example = ({title}) => {
  const initialState = {
    name: 'John',
    age: 25
  };
  const [{name, age}, setFormState] = useState(initialState);
  const handleChange = key => e => {
    const newValue = e.target.value
    setFormState(oldState => ({
      ...oldState,
      [key]: newValue
    }));
  };
  return (
    <form onSubmit={e=>e.preventDefault()}>
      <input type='text' id='name' name='name' placeholder={name} onChange={handleChange('name')} />
                <p>The person's name is {name}.</p>
                <br />
                <label htmlFor='age'>Age: </label>
                <input type='text' id='age' name='age' placeholder={age} onChange={handleChange('age')} />
                <p>His/her age is {age}.</p>
    </form>
  );
};

// Render it
ReactDOM.render(
  <Example />,
  document.getElementById("react")
);

或者,如果您需要一个具有更大灵活性的函数工厂(例如,如果您试图在其中存储其他状态),则可以使用useReducer()而不是useState(),然后使用调度函数。作为一个未经测试的粗略例子

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>

最后,有可能 完全按照您的意思进行操作,并具有一个仅使用ref即可更新状态的函数,无论谁调用它。但这不是推荐的方法。