0
votes

SetState ne met pas à jour l'état

J'essaie de rapporter des composants dans app.js code> lorsqu'un utilisateur se connecte mais quand i setState code> dans app.js code> (IN Commande de restituer des composants), l'état ne change pas d'une autre

Tout d'abord, j'envoie le nom d'utilisateur et le mot de passe de login.js code> à app.js code> ici: p> xxx pré>

ceci est rendu dans App.js ici: p> xxx pré>

Il doit appeler la fonction de connexion dans App.js (qui est terminée parce que I console.log (nom d'utilisateur) et il est reçu): p> xxx pré>

Lorsque cela est fait, connecté devrait devenir vrai, ce qui rend le lightningtalkcomponent code > Dans app.js code> (voir aussi): p> xxx pré>

L'état initial de l'application.js est-ce: p>

import React from 'react';
import LightningTalk from './components/lightning-talk-component.js';
import Form from './components/form.js';
import Login from './components/login.js';
import './App.css'


// initialized state of App to hold an empty lightningTalks compoennt. componentDidMount sets its state depends
class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      lightningTalks: [],
      username: "",
      loggedIn: false
    };
  }

// componentDidMount is called and sets the state of the lightningTalks array in constructor(props)
  componentDidMount = () => {
    fetch("http://localhost:4000/talks.json")
    .then(response => response.json())
    .then((data) => {
      // sorts the data when component mounts from largest to smallest votes
      data.sort((a, b) => b.votes - a.votes)
      this.setState((state) => {
        return {
          lightningTalks: data
        };
      });
    });
  }

// sends a post request to the API
  postInApp = (titleDescription) => {
    const talk = {}
    talk.title = titleDescription.title
    talk.description = titleDescription.description
    talk.votes = 0

      fetch("http://localhost:4000/talks", {
        headers: {
          "Content-Type": "application/json"
        },
        method: "POST",
        body: JSON.stringify({ "talk": talk })
        })
      .then(response => response.json())
      .then((data) => {
         console.log(data);
      });
  }

  login = (username) => {
    console.log('username', username)
    console.log('username state', this.state.username)

    this.setState({
      loggedIn: true,
      username: username
    });
    console.log('username', username)
    console.log('username state', this.state.username)

    console.log('logged in state', this.state.loggedIn)
  }

// increments/decrements the votes in an object of lightningTalks
  incrementInApp = (id) => {
    // creates a new array based off current state of lightningTalks
    const nextLightningTalks = this.state.lightningTalks.map((currentLightningTalk) => {
      // if the id in the parameters equals the id of the current objects ID then place back into the array
      if (currentLightningTalk.id !== id) {
        return currentLightningTalk
      }
      // whatever remains (the one whose ID does match), += 1 votes of that object
        const nextLightningTalk = {...currentLightningTalk, votes: currentLightningTalk.votes + 1,
        };
    return nextLightningTalk
    })
    // sorts when number of votes increases
    nextLightningTalks.sort((a, b) => b.votes - a.votes)
// set new state of lightningTalks to equal the result of the new array above (the .map)
  this.setState({lightningTalks: nextLightningTalks})
  }

  decrementInApp = (id) => {
    const nextLightningTalks = this.state.lightningTalks.map((currentLightningTalk) => {
      if (currentLightningTalk.id !== id) {
        return currentLightningTalk
      }
        const nextLightningTalk = {...currentLightningTalk, votes: currentLightningTalk.votes - 1,
        };
    return nextLightningTalk
    })
     // sorts when number of votes decreases
    nextLightningTalks.sort((a, b) => b.votes - a.votes)

  this.setState({lightningTalks: nextLightningTalks})
  }

  lightningTalkRender(props) {
    return <div className="talks">
      {this.state.lightningTalks.votes}
        {this.state.lightningTalks.map((talk) => {
          return <LightningTalk lightningTalk={talk} incrementInApp={this.incrementInApp} decrementInApp={this.decrementInApp}/>
                })}
            </div>
  }

  // now the state of lightning talks depends on what is on the API. Below there is a loop(.map) which is set by componentDidMount
  render() {
    return (
      <div className="container">
        <h1>Lightning Talks!</h1>
          <Login login={this.login}/>
            {this.state.loggedIn ? <lightningTalkRender/> : null}
          <h3 className="form-header"> Submit your talk</h3>
        <Form postInApp={this.postInApp}/>
      </div>
    )
  }
}

export default App;


import React from "react"
import './login.css';

class Login extends React.Component {
  constructor(props) {
    super(props);
    this.loginSubmit = this.loginSubmit.bind(this)
      this.state = {
        username: '',
        password: '',
        errors: [],
        pwdStrength: null
      }
  }

  showValidationErr (e, msg) {
    this.setState((prevState) => ( { errors: [...prevState.errors, { e, msg }] } ));
  }

  clearValidationErr (e) {
    this.setState((prevState) => {
      let newArr = [];
      for(let err of prevState.errors) {
        if(e !== err.e) {
          newArr.push(err);
        }
      }
      return {errors: newArr};
    })
  }

   onUsernameChange= (e) => {
    this.setState({ username: e.target.value })
    this.clearValidationErr("username");
   }

   onPasswordChange= (e) => {
    this.setState({ password: e.target.value })
    this.clearValidationErr("password");
    // set state of password strength based on length. Render these as CSS below
    if (e.target.value.length <= 8) {
      this.setState({ pwdStrength: "pwd-weak"})
    } if (e.target.value.length > 8) {
      this.setState({ pwdStrength: "pwd-medium" })
    } if (e.target.value.length > 12) {
      this.setState({ pwdStrength: "pwd-strong" })
    }
  }

 // on submit, time is logged (new Date) and state of title and description is changed
   loginSubmit= (e) => {
    e.preventDefault()

      if(this.state.username === "") {
        this.showValidationErr("username", "Username cannot be empty")
      } if (this.state.password === "") {
        this.showValidationErr("password", "Password cannot be empty")
      }


    const username = this.state.username
    this.props.login(username)
    // call onSubmit in LightningTalk so that new talk is added from form

    // this.props.postInApp(usernamePassword)
   }
   render() {

    let usernameErr = null;
    let passwordErr = null;

    for(let err of this.state.errors) {
      if(err.e === "username") {
        usernameErr = err.msg
      } if (err.e === "password") {
        passwordErr = err.msg
      }
    }

    return (
      <form className="form-container">
        <label>
        <p className="form-title">Username:</p>
          <input className="input-username"
          placeholder="enter your username"
          value={this.state.username}
          onChange={this.onUsernameChange}
          />
          <small className = "danger-error"> { usernameErr ? usernameErr : "" }</small>
        </label>
        <br />
        <label>
        <p className="form-description">Password:</p>
          <input className="input-password"
          placeholder="enter your password"
          value={this.state.password}
          onChange={this.onPasswordChange}
          type="password"
          />
          <small className="danger-error"> { passwordErr ? passwordErr : "" }</small>
          {this.state.password && <div className="password-state">
            <div
              className={"pwd " + (this.state.pwdStrength)}></div>
          </div>}
        {/*when the button is clicked, call the loginSubmit function above. E (event) is passed into loginSubmit function (above)*/}
        </label>
        <br />
        <button onClick={e => this.loginSubmit(e)}>Login</button>
      </form>
      );
    }
}

export default Login;


1 commentaires

3 Réponses :


1
votes

setState code> est async.iSi vous souhaitez voir l'état après la définition d'une fonction de rappel

<Login login={(username) => this.login(username)}/>


4 commentaires

Merci pour cette suggestion. Comment utiliser cela pour arriver à la source du problème?


Une fois que vous êtes sûr d'avoir les bonnes valeurs dans votre état, vous pouvez expliquer davantage votre méthode de connexion.


Je vois. Donc, je suis sûr que les bonnes valeurs proviennent de la méthode de connexion - c'est juste que l'état n'est pas mis à jour. Avez-vous une idée pourquoi?


Merci encore pour votre aide. Cela ne fonctionne toujours pas. D'autres suggestions?



0
votes
login = (username) => {
  this.setState({
    loggedIn: true,
    username: username
  });
  console.log('username state', this.state.username)
  console.log('logged in state', this.state.loggedIn)
}
You expect the state to have the new values when you console log above, but that is not how state works in React. React batches state updates, so when you call setState, the state is not updated immediately. Inside the login function, state.loggedIn will always have the old value. You have to wait for a rerender before the value is changed.Is your component rerendering? Or are you assuming that state is not updating because when you console log, the state has the old value?Try putting a console log in the top of your render function, and see if the new state gets logged correctly there, as the component will rerender with the new state.

3 commentaires

Intéressant, merci pour cela. Quand je console.log en rendu, il montrent. Cela signifie donc que j'ai besoin de renverser la composante, non?


Je suis nouveau pour réagir et coder, donc pas sûr de quoi faire


Le composant redevient automatiquement lorsque l'état est mis à jour.



0
votes

HILLYBOB991, comme indiqué dans ReactJS Docs. La fonction STSTATE () est une fonction ASYNC et votre code continue donc à votre relevé de journal, même avant que l'instate est exécutée.

login = (username) => {
console.log('username', username)
console.log('username state', this.state.username)

this.setState({
  loggedIn: true,
  username: username
}, () => {
  //WHATEVER YOU WANT TO BE EXECUTED AFTER SETTING STATE..
  //MAY BE YOUR CONSOLE LOG STATEMENT TOO
  const username = this.state.username
  this.props.login(username)

});


6 commentaires

Merci pour ça. Donc, le problème est que l'État ne met pas à jour initialement. Comment puis-je obtenir l'état de mettre à jour? Pour le moment il ne le fait pas


@ HILLYBOB991, l'État met à jour sur la go, c'est juste qu'il a le format ASYNC !! Tout ce que vous avez à faire est de mettre votre code de code suivant comme une fonction de rappel dans la fonction SetState elle-même!


Merci, je comprends simplement cette pièce de code figure dans le fichier App.js et non le composant de connexion. Dois-je littéralement cela dans le fichier login.js alors? Désolé, je suis nouveau à coder et à réagir, alors pas sûr!


@ HILLYBOB991, comme je peux voir dans le code intégral est ici une partie de votre code. Votre état est dans votre fichier login.js !! Alors oui cela va dans votre fichier login.js! Où vous pouvez définir le nouvel état, puis procéder plus loin avec ce nouvel état via la fonction de rappel. Et ne vous inquiétez pas, nous sommes ici pour s'entraider!


Merci pour cette ashutosh. Malheureusement, cela ne fonctionne toujours pas. Avez-vous d'autres suggestions?


@ hillybob991! Désolé mon pote c'est tout ce que j'avais.