我有以下反应组件:
导出默认类MyComponent扩展了React.Component{
提交(e){
e、 预防默认值();
var title=this.title;
控制台日志(标题);
}
render(){
返回(
…
<;form className=“form horizontal”>;
…
<;input type=“text”className=“form control”ref={(c)=>;this.title=c}name=“title”/>;
…
<;/form>;
…
<;button type=“button”onClick={this.onSubmit}className=“btn”>;保存<;/button>;
…
);
}
};
控制台给了我未定义的-你知道这个代码有什么问题吗
这里有三个答案,这取决于你(被迫)使用的React版本,以及你是否想使用钩子
第一件事:
理解React是如何工作的很重要,这样你才能正确地做事(protip:React网站上的React教程练习非常值得一读。它写得很好,涵盖了所有的基础知识,以实际解释如何做事)。这里的“正确”表示您正在编写一个应用程序界面,该界面恰好在浏览器中呈现;所有的界面工作都发生在React中,而不是“如果你在写网页,你习惯了什么”(这就是React应用程序是“应用程序”,而不是“网页”的原因)
React应用程序的呈现基于以下两个方面:
- 由创建该组件实例的父级声明的组件属性,父级可以在其整个生命周期中修改该组件的实例,以及
- 组件自身的内部状态,它可以在其整个生命周期中修改自身
当您使用React时,您明确地没有做的是生成HTML元素,然后使用这些元素:当您告诉React使用<;输入>,例如,您正在创建一个HTML输入元素,您正在告诉React创建一个React输入对象,该对象恰巧以HTML输入元素的形式呈现,其事件处理查看HTML元素的输入事件,但不受其控制
使用React时,您要做的是生成应用程序UI元素,向用户呈现(通常可操作)数据,用户交互会更改组件的状态,这可能会导致重新呈现部分应用程序界面以反映新状态。在此模型中,状态始终是最终权限,而不是“用于呈现它的任何UI库”,它在web上是浏览器的DOM。DOM在这个编程模型中几乎是事后才想到的:它只是React碰巧使用的特定UI框架
因此,对于输入元素,逻辑是:
- 键入输入元素
- 您的输入元素尚未发生任何变化,事件被React拦截并立即终止
- React将事件转发到您为事件处理设置的函数
- 该函数可以调度状态更新
- 如果是,React将运行该状态更新(异步!),并在更新后触发
render调用,但仅当状态更新更改了状态时 - 只有在完成此渲染后,UI才会显示您“键入了一封信”
所有这些都发生在几毫秒之内,如果不是更短的话,那么它看起来就像你在输入元素中键入的方式与“在页面上仅使用输入元素”相同,但绝对不是这样
因此,关于如何从React中的元素中获取值:
反应15及以下,使用ES5
为了正确地执行操作,您的组件有一个状态值,它通过输入字段显示,我们可以通过让UI元素将更改事件发送回组件来更新它:
var Component=React.createClass({
getInitialState:函数(){
返回{
输入值:“”
};
},
render:function(){
返回(
//...
<;输入值={this.state.inputValue}onChange={this.updateInputValue}/>;
//...
);
},
updateInputValue:函数(evt){
这是我的国家({
输入值:evt.target.value
});
}
});
因此,我们告诉React使用updateInputValue函数来处理用户交互,使用setState来计划状态更新,而render点击this.state.inputValue这一事实意味着,当它在更新状态后重新启动时,用户将根据键入的内容查看更新文本
基于评论的附录
假设UI输入表示状态值(考虑一下如果用户中途关闭选项卡,并且选项卡被恢复,会发生什么情况。他们填写的所有值都应该恢复吗?如果是,这就是状态)。这可能会让你觉得一个大表单需要几十个甚至一百个输入表单,但React是以一种可维护的方式对你的UI建模:你没有100个独立的输入字段,你有一组相关的输入,所以你在一个组件中捕获每个组,然后将你的“主”表单构建为一个组的集合
MyForm:
提供:
<;个人数据/>;
<;AppPreferences/>;
<;第三方/>;
...
这也比一个巨大的单一表单组件更容易维护。将组拆分为具有状态维护的组件,其中每个组件一次只负责跟踪几个输入字段
您可能还觉得编写所有这些代码是“一件麻烦事”,但这是一个错误的节省:不是您的开发人员,包括未来的您,实际上从看到所有这些输入显式连接中获益匪浅,因为它使代码路径更容易跟踪。但是,您始终可以进行优化。例如,您可以编写一个状态链接器
MyComponent=React.createClass({
getInitialState(){
返回{
firstName:this.props.firstName | |“”,
lastName:this.props.lastName | |“
...: ...
...
}
},
组件willmount(){
Object.keys(this.state).forEach(n=>{
设fn=n+‘改变’;
此[fn]=evt=>{
让update={};
更新[n]=evt.target.value;
此.setState(更新);
});
});
},
render:function(){
返回Object.keys(this.state).map(n=>{
<;输入
键={n}
type=“text”
值={this.state[n]}
onChange={this[n+'Changed']}/>;
});
}
});
当然,这有改进的版本,所以请点击https://npmjs.com 并搜索您最喜欢的React-state链接解决方案。开源主要是寻找其他人已经做过的事情,并使用它们,而不是自己从头开始编写
反应16(和15.5过渡)和“现代”JS
从React 16开始(从15.5开始为软启动),不再支持createClass调用,需要使用类语法。这改变了两件事:明显的类语法,以及createClass可以“免费”执行的This上下文绑定,因此为了确保事情仍然有效,请确保在处理程序中为This上下文保护匿名函数使用“胖箭头”符号,例如我们在这里的代码中使用的onChange:
类MyComponent扩展了React.Component{
建造师(道具){
超级(道具);
此.state={
输入值:“”
};
}
render(){
返回(
//...
<;输入值={this.state.inputValue}onChange={evt=>;this.updateInputValue(evt)}/>;
//...
);
},
更新输入值(evt){
这是我的国家({
输入值:evt.target.value
});
}
});
您可能还看到人们在构造函数中为所有事件处理函数使用bind,如下所示:
构造函数(道具){
超级(道具);
this.handler=this.handler.bind(this);
...
}
render(){
返回(
...
<;element onclick={this.handler}/>;
...
);
}
不要那样做
几乎在您使用bind的任何时候,“您做错了”这句谚语都适用。您的类已经定义了对象原型,因此已经定义了实例上下文。不要把bind放在上面;使用普通事件转发,而不是复制构造函数中的所有函数调用,因为这种复制会增加错误的表面,并使跟踪错误变得更加困难,因为问题可能在构造函数中,而不是在调用代码的地方。以及将维护负担强加给你(拥有或选择)与之共事的其他人
是的,我知道医生说没事。不是,不要这样做
反应16.8,使用带挂钩的功能部件
从React 16.8开始,通过使用钩子,函数组件(即,实际上只是一个将一些道具作为参数的函数,可以像组件类的实例一样使用,而无需编写类)也可以被赋予状态
如果您不需要完整的类代码,而只需要一个实例函数,那么现在可以使用useState钩子获取一个状态变量及其更新函数,其工作原理与上述示例大致相同,只是没有setState函数调用:
从“react”导入{useState};
函数myFunctionalComponentFunction(){
const[input,setInput]=useState(“”);/“”是初始状态值
返回(
<;div>;
<;label>;请指定:<;/label>;
<;input value={input}onInput={e=>;setInput(e.target.value)}/>;
<;/div>;
);
}
p