持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第8天,点击查看活动详情 列表&Key
列表
function App() {
const nums = [1,2,3,4,5]
const listLi = nums.map((num) => <li key={num}>{num}</li>)
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<ul>{listLi}</ul>
</header>
</div>
);
}
key
key帮助React识别哪些元素改变了,比如被添加或删除。因此要给数组中的每一个元素赋予一个确定的标识
一个元素的key最好是这个元素在列表中拥有的一个独一无二的字符串。通常,以数据中的id作为元素的key
当元素没有确定id的时候,用元素索引index作为key
用key提取组件
元素的key只有放在就近的数组上下文中才有意义
错误写法
function ListItem(props) {
const value = props.value
return(
<li key={value.toString()}>{value}</li>
)
}
function NumberList(props){
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<ListItem value={number} />
);
return (
<ul>{listItems}</ul>
)
}
正确写法
function ListItem(props) {
const value = props.value
return(
<li>{props.value}</li>
)
}
function NumberList(props){
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<ListItem key={number.toString()} value={number} />
);
return (
<ul>{listItems}</ul>
)
}
key值在兄弟节点之间必须唯一
数组元素中使用的key在其兄弟节点之间应该是独一无二的。然而,它们不需要是全局唯一的。当生成两个不同的数组时,可以使用相同的key值
key会传递信息给React,但不会传递给组件。组件需要使用key属性的值,需要用其他属性显式传递值
JSX中嵌入map()
JSX允许在大括号中嵌入任何表达式,所以可以内联map()返回结果
function NumberList(props){
const numbers = props.numbers;
return (
<ul>{numbers.map((number)=>{
<ListItem key={number.toString()} value={number}></ListItem>
})}</ul>
)
}
表单
在React里,HTML表单元素的工作方式和其他的DOM元素有些不同,这是因为表单元素通常会保持一些内部的state。纯HTML表单只接收一个名称:
<form>
<label>名称:
<input type="text" name="name">
</label>
<input type="submit" value="提交">
</form>
此表单具有默认的HTML表单行为,即在用户提交表单后跳转到新页面。在React中执行相同的代码,依旧有效,但大多数情况下,使用JavaScript函数可以很方便的处理表单的提交,同时还可以访问用户填写的表单数据。实现这种效果的标准方式是使用“受控组件”
受控组件
在HTML中,表单元素(如、和)通常自己维护state,并根据用户输入进行更新。而在React中,可变状态通常保存在组件的state属性中,并且只能通过使用setState()来更新。
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) { this.setState({value: event.target.value}); }
handleSubmit(event) {
alert('提交的名字: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}> <label>
名字:
<input type="text" value={this.state.value} onChange={this.handleChange} /> </label>
<input type="submit" value="提交" />
</form>
);
}
}
受控组件输入的值始终由React的state驱动。
textarea标签
HTML中,元素通过其子元素定义:
<textarea>这里是文本</textarea>
React中,使用value属性代替。
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: '',
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('提交' + this.state.value);
event.preventDefault();
}
render() {
return (
<div className="App">
<form onSubmit={this.handleSubmit}>
<label>
提交:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="提交" />
</form>
</div>
);
}
}
select标签
在HTML中,<select>创建下拉列表标签。
<select>
<option value="a">a</option>
<option selected value="b">b</option>
</select>
由于selected属性的缘故,b默认被选择。React并不会使用selected属性,而是在根select标签上使用value属性。这在受控组件中更便捷,只需在根标签中更新。
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 'a',
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('提交' + this.state.value);
event.preventDefault();
}
render() {
return (
<div className="App">
<form onSubmit={this.handleSubmit}>
<label>
提交:
<select value={this.state.value} onChange={this.handleChange}>
<option value="a">a</option>
<option value="b">b</option>
</select>
</label>
<input type="submit" value="提交" />
</form>
</div>
);
}
}
总结:input,textarea和select之类得标签都非常相似,都是接收value属性。
文件input标签
HTML中js<input type="file">允许用户从存储设备中选择一个或多个文件,将其上传到服务器,或通过使用JavaScript的File API进行控制
<input type="file"/>
因为value是只读,所以是React中的一个非受控组件。
处理多个输入
当需要处理多个input元素时,可以给每个元素添加name属性,并让处理函数根据event.target.name的值选择要执行的操作。
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
isOn: true,
nums: 2
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
render() {
return (
<form>
<label>
参与:
<input
name="isOn"
type="checkbox"
checked={this.state.isOn}
onChange={this.handleChange} />
</label>
<br />
<label>
数量:
<input
name="nums"
type="number"
value={this.state.nums}
onChange={this.handleChange} />
</label>
</form>
)
}
}
这里使用了ES6计算属性名称的语法更新给定输入名称对应的state值:
this.setState({
[name]: value
})
等同ES5
var state = {};
state[name] = value;
this.setState(state);
另外由于setState()自动将部分state合并到当前state,只需调用它更改部分state即可。
受控输入空值
在受控组件上指定value的prop会阻止用户更改输入。如果指定value,但输入仍可编辑,则可能是将value设置为undefined或null。
受控组件的替代品
有时使用受控组件会很麻烦,因为需要为数据变化的每种方式都编写事件处理函数,并通过一个React组件传递所有输入state。这时就需要到非受控组件
非受控组件
在大多数情况下,都是使用受控组件来处理表单数据。在一个受控组件中,表单数据是由React组件来管理的。另一种替代方案就是使用非受控组件,非受控组件中,表单数据将交由DOM节点来进行处理。
编写一个非受控组件,而不是为每个状态更新都编写数据处理函数,使用ref来从DOM节点中获取表单数据。
class App extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.input = React.createRef();
}
handleSubmit(event){
alert('aa'+this.input.current.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
姓名:
<input type="text" ref={this.input}></input>
</label>
<input type="submit" value="提交"></input>
</form>
)
}
}
非受控组件将真实数据存储在DOM节点中,所以在使用非受控组件能更容易同时集成React和非React代码。