[英]Converting React Class to Functional Components with Hooks. How to fix “uncontrolled input” error
I have the following code that I understand and it works perfectly.It is a class component. 我有以下我理解的代码,它可以完美运行,它是一个类组件。 I am trying to write a working function component version with hooks.
我正在尝试编写具有钩子的工作功能组件版本。 My refactor is giving me the infamous "uncontrolled input" error.
我的重构给了我一个臭名昭著的“不受控制的输入”错误。 I did not have the error until I used
useEffect
. 在使用
useEffect
之前,我没有出现错误。 I want to know how to fix the error. 我想知道如何解决该错误。 Thank you.
谢谢。
Old (Working) Class Component 旧(工作)类组件
class Dashboard extends Component{
constructor(props){
super(props)
this.state = {
clients: [],
clientName:undefined
}
this.clientsRef = firebase.database().ref('clients')
}
handleChange = (evt) => {
this.setState({
[evt.target.name]: evt.target.value
});
console.log(this.state.clientName);
}
addNewClient=(event,userID)=>{
event.preventDefault()
this.clientsRef.push({
name: this.state.clientName,
user_id:userID
});
this.setState({
clientName:""
})
}
componentDidMount() {
this.clientsRef.on('child_added', snapshot => {
const client = snapshot.val();
client.key = snapshot.key;
this.setState({ clients: [...this.state.clients, client]})
});
// this.state.clients.concat(client)
}
/*_______________________________________________
Add new company and associate it with a specific user ID
_________________________________________________*/
//
render(){
const {classes} = this.props;
return(
<Consumer>
{(userData)=>{
if(userData.state.user){
console.log(userData.state.user);
}
return(
<div className={classes.root}>
<Grid container spacing={24}>
<Grid item xs={12}>
<Paper>
</Paper>
</Grid>
<Grid item xs={12} sm={4}>
</Grid>
<Grid item xs={12} sm={4}>
<Paper className={classes.paper}>
<div>
<h1>Add new client or company</h1>
<form onSubmit = {(event)=>this.addNewClient(event,userData.state.userID)}>
<input type="text" name="clientName" onChange = {this.handleChange} value ={this.state.clientName}/>
<input type="submit" value="add client"/>
</form>
<ul>
{
this.state.clients.map((val,index)=>{
if(userData.state.userID === val.user_id){
return <a key={index} href={`/dashboard/${userData.state.userID}/company/${val.name}/${val.key}`}>
<ListItem>{val.name}</ListItem>
</a>
}
})
}
</ul>
</div>
</Paper>
</Grid>
<Grid item xs={12} sm={4}>
</Grid>
</Grid>
</div>
)
}
}
</Consumer>
)
}
}
Functional Component Version that Gives the Error. 给出错误的功能组件版本。
function Dashboard(props){
const initialState = {
clients: [],
clientName:""
}
const [formState,setForm] = useState(initialState);
const clientsRef = firebase.database().ref('clients');
function handleChange(evt){
setForm({...formState,
[evt.target.name]: evt.target.value
});
console.log(formState)
}
function addNewClient(event,userID){
event.preventDefault();
clientsRef.push({
name: formState.clientName,
user_id:userID
});
setForm({
clientName:""
})
}
function fetchData() {
clientsRef.on('child_added', snapshot => {
const client = snapshot.val();
console.log(client)
client.key = snapshot.key;
setForm({ clients: [...formState.clients, client]})
});
}
useEffect(()=>{
fetchData()
},[])
/*_________________*/
const {classes} = props;
const userData = useContext(Context);
return(
<div className={classes.root}>
<Grid container spacing={24}>
<Grid item xs={12}>
<Paper>
</Paper>
</Grid>
<Grid item xs={12} sm={4}>
</Grid>
<Grid item xs={12} sm={4}>
<Paper className={classes.paper}>
<div>
<h1>Add new client or company</h1>
<form onSubmit={(event)=>addNewClient(event,userData.state.userID)} >
<input type="text" name="clientName" onChange = {handleChange} value = {formState.clientName}/>
<input type="submit" value="add client"/>
</form>
<ul>
{
formState.clients.map((val,index)=>{
if(userData.state.userID === val.user_id){
return <a key={index} href={`/dashboard/${userData.state.userID}/company/${val.name}/${val.key}`}> <ListItem>{val.name}</ListItem></a>
}
})
}
</ul>
</div>
</Paper>
</Grid>
<Grid item xs={12} sm={4}>
</Grid>
</Grid>
</div>
)
}
The updator, setForm
, returned from useState
works differently from this.setState
. 该更新器,
setForm
,从返回useState
从工作方式不同this.setState
。
While this.setState
in Class Component automatically merges existing states , the hook's updator ( setForm
) doesn't automerge existing state . 虽然Class Component中的
this.setState
自动合并现有状态 ,但是挂钩的更新程序( setForm
) 不会自动合并 现有状态 。
... However, unlike this.setState in a class, updating a state variable always replaces it instead of merging it.
...但是,与类中的this.setState不同,更新状态变量总是替换它而不是合并它。
When you set clients in fetchData
, 在
fetchData
设置客户端时,
function fetchData() {
clientsRef.on("child_added", snapshot => {
const client = snapshot.val();
console.log(client);
client.key = snapshot.key;
// 👇
setForm({ clients: [...formState.clients, client] });
});
}
setForm({ clients: [...formState.clients, client] });
turns clientName
into undefined
. 将
clientName
变成undefined
。
You'd need to spread existing state (as you do with reducer or Redux) to return a new reference containing clientName
, which was set to the value for input
below. 您需要散布现有状态(与reducer或Redux一样),以返回包含
clientName
的新引用,该引用已设置为以下input
的值。
<input
type="text"
name="clientName"
onChange={handleChange}
value={formState.clientName}
/>;
When you fetch data, that input field gets uncontrolled
because formState.clientName
is undefined. 当您获取数据时,该输入字段将
uncontrolled
因为formState.clientName
是未定义的。
So return a new reference without clearing clientName
. 因此,返回一个新引用而不清除
clientName
。
setForm(previousForm => ({
...previousForm,
clients: [...formState.clients, client]
}));
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.