反应JS |为啥在输入更改时更新此状态挂钩会清除输入以防止写入任何内容?

Posted

技术标签:

【中文标题】反应JS |为啥在输入更改时更新此状态挂钩会清除输入以防止写入任何内容?【英文标题】:ReactJS | Why does updating this state hook on input change clear the input so as to prevent anything from being written?反应JS |为什么在输入更改时更新此状态挂钩会清除输入以防止写入任何内容? 【发布时间】:2021-10-07 08:45:08 【问题描述】:

我正在尝试创建一个简单的表单,它接受两个字符串,将它们存储在状态挂钩中,并自行清除onSubmit,但似乎通过输入的onChange(即onChange=(t) => setUserSide(t.target.value))更新状态挂钩有清除刚刚键入的内容的效果,导致字段不可用。当这行被删除时,输入会保留数据,当然,它不再存储在状态钩子中。

我的理解是/是通过相应的 setter 方法更新状态钩子不会产生副作用,尽管这里似乎并非如此。不过,在一个更简单的测试应用程序中,一切都按预期工作。

我认为相关的代码部分

钩子:

const [newWager, setWager] = useState(0);
const [userSide, setUserSide] = useState(0);     

表格:

const Home = () => 
    return(
      <div className="main">
      <div className="upper">
      1: retrievedSide1 : potFor WC<br/> 2: retrievedSide2 : potAgainst WC
      </div>
      <div className="card">
        <form className="form" id="submitWagerForm" autocomplete="off" onSubmit=wager>
          <label>
            Enter your wager and side:
            <br />
            <input
              className="input"
              type="text"
              name="amount"
              placeholder="# of WC"
              onChange=(t) => setWager(t.target.value)
            />
            <input
              className="input"
              type="text"
              name="side"
              placeholder="1 or 2"
              onChange=(t) => setUserSide(t.target.value)
            />
          </label>
          <button className="button" type="submit" value="Submit">
            Submit
          </button>
        </form>

提交:

const wager = async (t) => 
    t.preventDefault(); 
    try
      const accounts = await window.ethereum.enable();
      const account = accounts[0];
      const _wager = web3.utils.toWei(newWager);

      const gas = await eventWagerContract.methods.wager(userSide, _wager).estimateGas();
      const post = await eventWagerContract.methods.wager(userSide, _wager).send( from: account, gas );
      getUserWager(t);
      getCurrentPot(t);
    
    catch(e)
    
      alert('Apparently this is the best way to display blockchain errors :/\n\n' + e.message);
    
    var form = document.getElementById("submitWagerForm");
    form.reset();
  ;

App.js 的其余部分

从完整的应用程序中删除,但仍然表现出有问题的行为

import logo from './logo.svg';
import React,  useState  from "react";
import './App.css';
import  eventWager  from './abi/abi';
import  token  from './abi/abi';
import Web3 from "web3";
import  BrowserRouter as Router, Route, Switch  from 'react-router-dom';

const web3 = new Web3(Web3.givenProvider);
const contractAddress = "0x73A6Da02A8876C3E01017fB960C912dA0a423817";
const tokenAddress = "0x02F682030814F5AE7B1b3d69E8202d5870DF933f";
const eventWagerContract = new web3.eth.Contract(eventWager, contractAddress);
const tokenContract = new web3.eth.Contract(token, tokenAddress);

function App() 
  // Getter hooks
  const [retrievedWager, setRetrievedWager] = useState(0);
  const [currentPot, setRetrievedCurrentPot] = useState(0);
  const [potFor, setPotFor] = useState(0);
  const [potAgainst, setPotAgainst] = useState(0);
  const [retrievedUserSide, setRetrievedUserSide] = useState(0);
  const [retrievedSide1, setRetrievedSide1] = useState("1");
  const [retrievedSide2, setRetrievedSide2] = useState("2");
  const [requestAddress, setRequestAddress] = useState(0);
  const [requestAmount, setRequestAmount] = useState(0);

  // Setter hooks
  const [newWager, setWager] = useState(0);
  const [userSide, setUserSide] = useState(0);
  const [winningSide, setWinningSide] = useState(0);

  // Getter methods
  const getUserWager = async (t) => 
    if(t)  t.preventDefault(); 
    const accounts = await window.ethereum.enable();
    const account = accounts[0];
    const post = await eventWagerContract.methods.getWager(account).call();
    const _wager = web3.utils.fromWei(post);
    setRetrievedWager(_wager);
  ;

  const getCurrentPot = async (t) => 
    if(t)  t.preventDefault(); 
    const post = await eventWagerContract.methods.getPot().call();
    const _pot = web3.utils.fromWei(post);
    setRetrievedCurrentPot(_pot);
  ;
  
  // Setter methods
  const wager = async (t) => 
    t.preventDefault(); 
    try
      const accounts = await window.ethereum.enable();
      const account = accounts[0];
      const _wager = web3.utils.toWei(newWager);

      const gas = await eventWagerContract.methods.wager(userSide, _wager).estimateGas();
      const post = await eventWagerContract.methods.wager(userSide, _wager).send( from: account, gas );
      getUserWager(t);
      getCurrentPot(t);
    
    catch(e)
    
      alert('Apparently this is the best way to display blockchain errors :/\n\n' + e.message);
    
    var form = document.getElementById("submitWagerForm");
    form.reset();
  ;

  const allowSpend = async (t) => 
    t.preventDefault();
    try
      const accounts = await window.ethereum.enable();
      const account = accounts[0];
      const gas = tokenContract.methods.approve(contractAddress, web3.utils.toWei('9999')).estimateGas();
      const post = tokenContract.methods.approve(contractAddress, web3.utils.toWei('9999')).send( from: account );
    
    catch(e)
    
      alert('Apparently this is the best way to display blockchain errors :/\n\n' + e.message);
    
  ;

  const getCurrentSides = async (t) => 
    if(t)  t.preventDefault(); 
    const post = await eventWagerContract.methods.getSides().call();
    // const post = await eventWagerContract.methods.getPot().call();
    var substrings = post.split('||&&||');
    setRetrievedSide1(substrings[0]);
    setRetrievedSide2(substrings[1]);
  ;

  const Home = () => 
    return(
      <div className="main">
      <div className="upper">
      1: retrievedSide1 : potFor WC<br/> 2: retrievedSide2 : potAgainst WC
      </div>
      <div className="card">
        <form className="form" id="submitWagerForm" autocomplete="off" onSubmit=wager>
          <label>
            Enter your wager and side:
            <br />
            <input
              className="input"
              type="text"
              name="amount"
              placeholder="# of WC"
              onChange=(t) => setWager(t.target.value)
            />
            <input
              className="input"
              type="text"
              name="side"
              placeholder="1 or 2"
              onChange=(t) => setUserSide(t.target.value)
            />
          </label>
          <button className="button" type="submit" value="Submit">
            Submit
          </button>
        </form>
        <br />
        <div>
          <button className="button" onClick=getUserWager type="button">
            Your current wager: 
          </button>
            retrievedWager
        </div>
        <br /> <br />
        <div>
          <button className="button" onClick=getCurrentPot type="button">
            Click for current pot
          </button>
          currentPot
        </div>
        <br /><br />
        <div>
          <button className="button" onClick=allowSpend type="button">
              Click to approve
          </button>
        </div>
      <div className="lower">
        <form className="form" onSubmit=getCurrentSides>
          <label>
              <button className="button" type="submit" value="Submit">
                Get Sides
              </button>
            </label>
          </form>
        </div>
      </div>
    </div>
    );
  

  return (
    <Router>
      <Switch>
        <Route exact path="/">
          <Home />
        </Route>
        <Route exact path="/admin">
          /* <Admin /> */
        </Route>
      </Switch>
    </Router>
  );


export default App;

更简单的测试应用

import logo from './logo.svg';
import './App.css';
import React,  useState  from "react";


function App() 

  const [newWager, setWager] = useState(0);
  const [userSide, setUserSide] = useState(0);
  const [retrievedSide1, setRetrievedSide1] = useState("1");
  const [retrievedSide2, setRetrievedSide2] = useState("2");

  const wager = async (t) => 
    if(t)  t.preventDefault(); 
    else  console.log('No t') ;
    let str = newWager + ' on side ' + userSide;
    wagerComplete(str);
    let form = document.getElementById("submitWagerForm");
    form.reset();
  ;

  const wagerComplete = async (wagerstr) => 
    console.log(wagerstr);
    alert(wagerstr);
  ;

  const beginRound = async (t) => 
    t.preventDefault();
    // setRetrievedSide1(document.getElementById("side1").value);
    // setRetrievedSide2(document.getElementById("side2").value);
    try
      // const accounts = await window.ethereum.enable();
      // const account = accounts[0];
      // const gas = await eventWagerContract.methods.beginRound(retrievedSide1, retrievedSide2).estimateGas();
      // const post = await eventWagerContract.methods.beginRound(retrievedSide1, retrievedSide2).send( from: account, gas );
      alert(retrievedSide1 + ' ' + retrievedSide2);
      wager();
    
    catch(e)
    
      alert('Apparently this is the best way to display blockchain errors :/\n\n' + e.message);
    
    let form = document.getElementById("beginRoundForm");
    form.reset();
  ;

  return (
    <div className="App">
      <form className="form" id="submitWagerForm" autocomplete="off" onSubmit=wager>
          <label>
            Enter your wager and side:
            <br />
            <input
              className="input"
              type="text"
              name="amount"
              placeholder="# of WC"
              onChange=(t) => setWager(t.target.value)
            />
            <input
              className="input"
              type="text"
              name="side"
              placeholder="1 or 2"
              onChange=(t) => setUserSide(t.target.value)
            />
          </label>
          <button className="button" type="submit" value="Submit">
            Submit
          </button>
        </form>
        <form className="form" id="beginRoundForm" autocomplete="off" onSubmit=beginRound>
            <label>
              <input
                className="input"
                type="text"
                name="name"
                id="side1"
                placeholder="Side 1"
                onChange=(t) => setRetrievedSide1(t.target.value)
              />
              <input
                className="input"
                type="text"
                name="side"
                placeholder="Side 2"
                id="side2"
                onChange=(t) => setRetrievedSide2(t.target.value)
              />
              <button className="button" type="submit" value="Submit">
                Begin Round
              </button>
            </label>
          </form>
    </div>
  );


export default App;

【问题讨论】:

您需要将输入中的值设置为钩子值 @GarrGodfrey 这并不完全符合我的要求。使用这样设置的值,提交后输入不会清除,现在尝试保留钩子的默认值。为什么不在测试应用程序中设置必要的值?因为它的行为与我预期的一样,无需设置值。 @GarrGodfrey 在这里是正确的,阅读受控输入here。 “......现在尝试保留钩子的默认值” - 这不是你想要的吗?正常的提交行为将提交表单(即,将适当的数据发送到要处理该数据的任何服务)并刷新页面,从而导致对 useState 的新调用,初始值为 0(您可以考虑将其设为 '',但在这里无关紧要)对于每个输入。 【参考方案1】:

看来我应该花更多时间在文档上,useRef 得到了我想要的行为。

【讨论】:

以上是关于反应JS |为啥在输入更改时更新此状态挂钩会清除输入以防止写入任何内容?的主要内容,如果未能解决你的问题,请参考以下文章

在状态转换错误期间无法更新-导航到另一个屏幕时使用挂钩对本机做出反应

我可以在 React js 中使用 useRef 挂钩而不是 onChange 事件来更新状态吗?

无法对未安装的组件执行 React 状态更新(useEffect 反应挂钩)

更改输入的状态会延迟一个字符(useState 挂钩)

谷歌地图与反应 js 挂钩的集成,地图在更新时闪烁

当状态改变时,对文本字段做出反应 onChange 更新整个组件