当用户登录时,我正在尝试在App.js
中渲染组件,但是当我在setState
中App.js
(以渲染组件)时,状态不会改变>
首先,我将用户名和密码从login.js
发送到App.js
:
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)
这是在App.js中呈现的:
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>
)
}
它应该在App.js中调用登录函数(之所以这样做,是因为我console.log(username)并收到了):
login = (username) => {
console.log('username', username)
console.log('username state', this.state.username)
this.setState({
loggedIn: true,
username: username
});
console.log('username', username) // username typed in is logged
console.log('username state', this.state.username) //username in state is empty
console.log('logged in state', this.state.loggedIn) // logged-In is still false
}
完成此操作后,登录应该变为true,这将在lightningTalkComponent
中显示App.js
(另请参见上文):
{this.state.loggedIn ? <lightningTalkRender/> : null}
App.js的初始状态为:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
lightningTalks: [],
username: "",
loggedIn: false
};
}
为什么状态没有更新?
完整代码在这里:
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;
答案 0 :(得分:1)
setState
是异步的。如果要在设置后查看状态,请使用回调函数
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)
});
}
登录功能需要一个参数,因此将其作为道具传递的正确方法是
<Login login={(username) => this.login(username)}/>
答案 1 :(得分:0)
login = (username) => {
this.setState({
loggedIn: true,
username: username
});
console.log('username state', this.state.username)
console.log('logged in state', this.state.loggedIn)
}
当您在上面进行控制台日志时,您希望状态具有新值,但这不是状态在React中的工作方式。反应批处理状态更新,因此,当您调用setState
时,状态不会立即更新。在login
函数内部,state.loggedIn
将始终具有旧值。您必须等待重新渲染,然后再更改值。
您要重新渲染组件吗?还是假设状态未更新是因为在您控制台日志时状态为旧值?
请尝试将控制台日志放在渲染功能的顶部,并查看新状态是否正确记录在该位置,因为组件将使用新状态重新呈现。
答案 2 :(得分:0)
hillybob991,如ReactJS文档中所述。 setState()函数是一个异步函数,因此即使在执行setState之前,您的代码也会继续执行您的log语句。
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) //THIS LINE WOULD BE EXECUTED EVEN THOUGH YOUR SETSTATE IS NOT EXECUTED
因此,应该在代码中添加一个回调函数。以下代码正是您想要的。
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)
});
}