0

我在本地 testrpc 区块链上部署了一个可靠的合约。我所有的合约方法测试都已签出,但处理 Web3 事务并相应地更新状态给我带来了麻烦。

当我添加一个用户帐户时,我的下一个操作是返回我的合同的所有用户帐户。以及...更新我的状态(RegisteredAccounts)。但是,通过我的一系列承诺,我没有看到我的状态更新。我知道 setState 也是异步的,所以如何在不刷新页面或调用 ComponentDidMount() 的情况下查看我的状态更新?

这是我的 Solidity Accounts 合同(到目前为止我处理的部分

pragma solidity ^ 0.4.4;
contract Accounts {

  mapping(address => User) public mUsers;
  address[] public Users; //users whitepages

  struct User {
    string handle;
    bytes32[] taskList;
  }

  function addNewUser(string _handle) returns(bool success) {
    address newUserAddr = msg.sender;

    //if handle not in userAddresses & the handle is not null
    if (bytes(mUsers[newUserAddr].handle).length == 0 && bytes(_handle).length != 0) {
      mUsers[newUserAddr].handle = _handle;
      Users.push(newUserAddr);
      return true;
    } else {
      return false;
    }
  }

  function getUsers() constant returns(address[]) {
    return Users;
  }
}

这是我的 App 容器组件——相关部分 registerNewUser() 是我现在的问题孩子。

class App extends Component {
  state = {
    modalOpen: false,
    SenderAddress: null,
    RegisteredAccounts: [],
    isRegisteredUser: false,
    SenderTaskList: [], //not set
    AccountsCtrct: null,
    web3: null
  }
  //#region APP METHODS
  componentWillMount() {
    // Get network provider and web3 instance. -- See utils/getWeb3 for more info.
    getWeb3.then(results => {
      this.setState({
        web3: results.web3
      })
      this.instantiateContracts() //instantiate contract
    }).catch(() => {
      console.log('Error finding web3.')
    })
  }


  instantiateContracts() {
    this.setState({
      AccountsCtrct: contract(AccountsContract)
    })
    this.state.AccountsCtrct.setProvider(this.state.web3.currentProvider)

    //Get block chain addresses --- only returns the current address selected in metamask (web3 current addr)
    this.state.web3.eth.getAccounts((error, accounts) => {
      this.setState({
        SenderAddress: accounts[0]
      })

      //INIT ACCOUNTS CONTRACT
      var acctDeployed = this.state.AccountsCtrct.deployed()
      acctDeployed.then((instance) => {
        return instance.getUsers();
      }).then((res) => {
        this.setState({
          RegisteredAccounts: res
        })

        if (this.state.RegisteredAccounts.includes(this.state.SenderAddress)) {
          this.setState({
            isRegisteredUser: true
          })
        }
      })
    })
  }


  registerUser = (handle) => {
    var acctInstance
    this.state.AccountsCtrct.deployed().then((inst) => {

      //add current user to this account
      acctInstance = inst
      return acctInstance.addNewUser(handle, {
        from: this.state.SenderAddress
      });
    }).then(() => {

      //now we added our user -- update registeredAccounts setState
      //pass response users array to promise
      return acctInstance.getUsers()
    }).then(res => {

      this.setState({
        RegisteredAccounts: res
      })
      if (res.includes(this.state.SenderAddress)) {
        this.setState({
          isRegisteredUser: true
        })
      }
    })
  }

  toggleModal = () => {
    this.setState(prevState => ({
      modalOpen: !prevState.modalOpen
    }));
  }
  //#endregion
  
  render() {
    return (
      <div className="App">
        <nav className="navbar pure-menu pure-menu-horizontal">
            <a href="#" className="pure-menu-heading pure-menu-link">Truffle Box</a>
            {
              !this.state.isRegisteredUser
              ? <a style={navLink} onClick={ this.toggleModal } href="#" className="pure-menu-heading pure-menu-link">Register</a>
              : null
            }
        </nav>

        <ModalUserNav visible={this.state.modalOpen}
              toggleModal={this.toggleModal}
              isRegistered={this.state.isRegisteredUser}
              registerUser={this.registerUser} />
    );
  }
}

最后我的子组件

class ModalUserNav extends Component {
  state = {
    unpl: "UserName",
    pwpl: "Password",
    errorCode: 'Registration Failed',
    errorVisible: false
  }


  handleOnChangePL = (e) => {
    this.setState({
      [e.target.name]: e.target.value
    })
  }

  handleSubmit = () => {
    if (this.state.unpl !== "") {
      this.props.registerUser(this.state.unpl)
      this.props.toggleModal();
    } else {
      //if the input is empty update the error code and show
      console.log('registration failed!')
      this.setState({
        errorCode: 'REGISTRATION ERR: empty handles are not allowed!',
        errorVisible: true
      })
    }
  }

  render() {
    return (
      <section>
          <Modal visible={this.props.visible} effect="fadeInUp">
            <div className="pure-form">
              <fieldset style={modalFormView}>
                <legend style={{fontSize: "18px"}}><b>Register now. All you need is a handle!</b></legend>
                <div className="flexContainer">
                  <input style={{marginTop: "7px", height: "2.6em", marginLeft: "5px", marginRight: "5px"}} type="text" name="unpl" placeholder={this.state.unpl} onChange={(event) => {this.handleOnChangePL(event)}} value={this.state.unpl} />
                  <button style={btnStyle} type="submit" className="pure-button pure-button-primary" onClick={() => {this.handleSubmit()}}><b>Register</b></button>
                </div>
              </fieldset>
            </div>
          </Modal>
      </section>
    )
  }
}

简而言之,我想用 setState 来跟进我的 2 个异步任务(addNewUser、getUsers),这样我就可以自动更改我的 UI 而无需刷新。那么我做错了什么?请严厉...我需要知道。

谢谢!

4

1 回答 1

0

您应该移动instantiateContracts到,setState因为setState不会立即更新数据。https://reactjs.org/docs/react-component.html#setstate

  this.setState({
    web3: results.web3
  }, () => {
    this.instantiateContracts() //instantiate contract
  })

更新1:关于registerUser:应该是

  this.setState({
    RegisteredAccounts: res
  }, () => {
    if (res.includes(this.state.SenderAddress)) {
      this.setState({
        isRegisteredUser: true
      })
    }
  })
于 2017-11-01T05:43:21.327 回答