五、事件处理
1、react事件处理与DOM相似但语法有不同
- React 事件的命名采用小驼峰式(camelCase),而不是纯小写。
- 使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。
// 传统html
<button οnclick="activateLasers()">
Activate Lasers
</button>
// react
<button onClick={activateLasers}>
Activate Lasers
</button>
- 在 React 中不能通过返回 false 的方式阻止默认行为。你必须显式的使用 preventDefault 。
// 传统html
<a href="#" οnclick="console.log('The link was clicked.'); return false">
Click me
</a>
// react
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}
- 一般不需要使用 addEventListener 为已创建的 DOM 元素添加监听器。只需要在该元素初始渲染的时候添加监听器即可。
2、事件处理函数声明的方法
构造器绑定bind
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {
isToggleOn: true
}
// 为了在回调中使用 `this`,这个绑定是必不可少的
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
this.setState(state => ({
isToggleOn: !state.isToggleOn
}))
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
)
}
}
ReactDOM.render(<Toggle />, document.getElementById('root'
使用 class fields 语法
class LoggingButton extends React.Component {
// 此语法确保 `handleClick` 内的 `this` 已被绑定。
// 注意: 这是 *实验性* 语法。
handleClick = () => {
console.log('this is:', this);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
回调中使用箭头函数
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
// 此语法确保 `handleClick` 内的 `this` 已被绑定。
return (
<button onClick={() => this.handleClick()}>
Click me
</button>
);
}
在回调中使用箭头函数在大多数情况下没什么问题,但如果该回调函数作为 prop 传入子组件时,这些组件可能会进行额外的重新渲染。我们通常建议在构造器中绑定或使用 class fields 语法来避免这类性能问题。
3、向事件处理程序传递参数
// 箭头函数 事件对象显式传递
<button onClick={(e) => this.deleteRow(id, e)}>
Delete Row
</button>
// Function.prototype.bind 事件对象及更多参数隐式传递
<button onClick={this.deleteRow.bind(this, id)}>
Delete Row
</button>
六、条件渲染
在 React 中,你可以创建不同的组件来封装各种你需要的行为。然后,依据应用的不同状态,你可以只渲染对应状态下的部分内容。
1、使用if或条件运算符
function UserGreeting(props) {
return <h1>welcome back!</h1>
}
function GuestGreeting(props) {
return <h1>please sign up.</h1>
}
function Greeting(props) {
let isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />
}
return <GuestGreeting />
}
ReactDOM.render(<Greeting isLoggedIn={false} />, document.getElementById('ro
2、元素变量
使用变量来储存元素。 它可以帮助你有条件地渲染组件的一部分,而其他的渲染部分并不会因此而改变。
function LoginButton(props) {
return (<button onClick={props.onClick}>Login</button>)
}
function LogoutButton(props) {
return (<button onClick={props.onClick}>Logout</button>)
}
class LoginControl extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoggedIn: false
}
}
handleLoginClick() {
this.setState({
isLoggedIn: false
})
}
handleLogoutClick() {
this.setState({
isLoggedIn: true
})
}
render() {
let isLoggedIn = this.state.isLoggedIn;
let button;
if (isLoggedIn) {
button = <LoginButton onClick={() => this.handleLoginClick()} />
} else {
button = <LogoutButton
onClick= {() => this.handleLogoutClick()} />
}
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{button}
</div>
)
}
}
ReactDOM.render(<LoginControl />, document.getElementById('ro
3、与运算符&&
true && expression 总是会返回 expression, 而 false && expression 总是会返回 false。
因此,如果条件是 true,&& 右侧的元素就会被渲染,如果是 false,React 会忽略并跳过它。
render() {
let isLoggedIn = this.state.isLoggedIn;
let button;
if (isLoggedIn) {
button = <LoginButton onClick={() => this.handleLoginClick()} />
} else {
button = <LogoutButton onClick={() => this.handleLogoutClick()} />
}
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{isLoggedIn && button}
</div>
)
}
4、三目运算符
另一种内联条件渲染的方法是使用 JavaScript 中的三目运算符 condition ? true : false。
render() {
let isLoggedIn = this.state.isLoggedIn;
let button = isLoggedIn ?
<LoginButton onClick={() => this.handleLoginClick()} />
:
<LogoutButton onClick={() => this.handleLogoutClick()} />
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{button}
</div>
)
5、阻止组件渲染
可以让 render 方法直接返回 null,而不进行任何渲染 。返回 null
并不会影响组件的生命周期
function WarningBanner(props) {
if (!props.warn) {
return null
}
return (<div>Warning!</div>)
}
class Page extends React.Component {
constructor(props) {
super(props)
this.state = {
showWarning: true
}
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
this.setState(state => ({
showWarning: !state.showWarning
}
));
}
render() {
return (
<div>
<WarningBanner warn={this.state.showWarning} />
<button onClick={this.handleClick} >
{this.state.showWarning ? 'hide' : 'show'}
</button>
</div>
)
}
}
ReactDOM.render(<Page />, document.getElementById('roo
七、列表与key
1、渲染多个组件
通过使用 {} 在 JSX 内构建一个元素集合
let numbers = [1, 2, 3, 4, 5]
const listItems = numbers.map((item) =>
<li>{item}</li>
)
ReactDOM.render(<ul>{listItems}</ul>, document.getElementById('root'))
2、基础列表组件
将前面的例子重构成一个组件
function NumberList(props) {
let numbers = props.numbers;
let listItems = numbers.map(item => <li>{item}</li>)
return (
<ul>{listItems}</ul>
)
}
let numbers = [1, 2, 3, 4, 5]
ReactDOM.render(<NumberList numbers={numbers} />, document.getElementById('root'))
此时会报错:Warning: Each child in a list should have a unique "key" prop.意思是要创建玩才能用。所以给每列元素分配一个key <li key={item.toString()}>{item}</li>
3、key
key 帮助 React 识别哪些元素改变了,比如被添加或删除。因此你应当给数组中的每一个元素赋予一个确定的标识。通常会使用数据中的 id 来作为元素的 key,万不得已你可以使用元素索引 index 作为 key
- key提取组件:元素的key只有放在就近数组的上下文才有意义。
- 一个好的经验法则是:在 map() 方法中的元素需要设置 key 属性。
- key 只是在兄弟节点之间必须唯一:当我们生成两个不同的数组时,我们可以使用相同的 key 值
- key会传递信息给React但不会传递给组件,要使用key值要用其他属性显式传递
4、在JSX中嵌入map()
JSX 允许在大括号中嵌入任何表达式
function ListItem(props) {
return (
<li>{props.value}</li>
)
}
// function NumberList(props) {
// let numbers = props.numbers;
// let listItems = numbers.map(number =>
// <ListItem key={number.toString()} value={number} />)
// return (
// <ul>{listItems}</ul>
// )
// }
// =============
function NumberList(props) {
let numbers = props.numbers
return (
<ul>
{
numbers.map(number => <ListItem key={number.toString()} value={number} />)
}
</ul>
)
}
let numbers = [11, 22, 33, 44, 55]
ReactDOM.render(<NumberList numbers={numbers} />, document.getElementById('root'))
八、表单
1、受控组件
在 HTML 中,表单元素(如<input>、 <textarea> 和 <select>)之类的表单元素通常自己维护 state,并根据用户输入进行更新。而在 React 中,可变状态(mutable state)通常保存在组件的 state 属性中,并且只能通过使用 setState()来更新。 把两者结合起来,使 React 的 state 成为“唯一数据源”。渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”。
class NameForm extends React.Component {
constructor(props) {
super(props)
this.state = {
name: ""
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this)
}
handleChange(event) {
this.setState({
name: event.target.value
})
}
handleSubmit(event) {
console.log('提交的名字' + this.state.name)
event.preventDefault()
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
名字:<input type="text" value={this.state.name} onChange={this.handleChange} />
</label>
<input type="submit" value="提交" />
</form>
)
}
}
ReactDOM.render(<NameForm />, document.getElementById('root
对于受控组件来说,输入的值始终由 React 的 state 驱动。
2、textarea标签
class TextArea extends React.Component {
constructor(props) {
super(props)
this.state = {
text: ""
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this)
}
handleChange(event) {
this.setState({
text: event.target.value
})
}
handleSubmit(event) {
console.log('提交的名字' + this.state.text)
event.preventDefault()
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
文章:<textarea type="text" value={this.state.text} onChange={this.handleChange} />
</label>
<input type="submit" value="提交" />
</form>
)
}
}
ReactDOM.render(<TextArea />, document.getElementById('root'
3、select标签
选中的实现--定义根select标签的value属性,而不使用selected
class SelectFrom extends React.Component {
constructor(props) {
super(props)
this.state = {
value: 3,
selectArr: []
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this)
}
handleChange(event) {
console.log('修改', event.target.value)
this.setState({
value: event.target.value
})
}
handleSubmit(event) {
console.log('提交的选择' + this.state.value)
event.preventDefault()
}
// 生命周期--挂载
componentDidMount() {
let selectArr = [
{
"code": 1,
"name": "a",
},
{
"code": 2,
"name": "b",
},
{
"code": 3,
"name": "c",
},
{
"code": 4,
"name": "d",
},
{
"code": 5,
"name": "e",
},
]
this.setState({
selectArr: selectArr,
})
}
render() {
let arr = this.state.selectArr
return (
<form onSubmit={this.handleSubmit}>
<label>
选择:
<select value={this.state.value} onChange={this.handleChange}>
{
arr.map((item, index) =>
< OptionItem key={index} value={item.code} name={item.name} />
)
}
</select>
</label>
<input type="submit" value="提交" />
</form>
)
}
}
function OptionItem(props) {
return (
<option value={props.value}>{props.name}</option>
)
}
ReactDOM.render(<SelectFrom />, document.getElementById('roo
select multiple 多选实现
4、处理多个输入
当需要处理多个 input 元素时,我们可以给每个元素添加 name 属性,并让处理函数根据 event.target.name 的值选择要执行的操作。
class MoreInput extends React.Component {
constructor(props) {
super(props);
this.state = {
name: "",
age: "",
check: true
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this)
}
handleChange(event) {
let target = event.target;
let name = target.name;
console.log(target.checked)
let value = target.name == 'check' ? target.checked : target.value
this.setState({
[name]: value
})
}
handleSubmit(event) {
console.log('提交:' + JSON.stringify(this.state))
event.preventDefault()
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
姓名:<input type="text" value={this.state.name} onChange={this.handleChange} name="name" />
</label>
<br />
<label>
年龄:<input type="text" value={this.state.age} onChange={this.handleChange} name="age" />
</label>
<br />
<label>
选择:<input type="checkbox" checked={this.state.check} onChange={this.handleChange} name="check" />
</label>
<br />
<label>
<input type="submit" value="提交" />
</label>
</form>
)
}
}
ReactDOM.render(<MoreInput />, document.getElementById('root
5、受控组件输入空值
在受控组件上指定 value 的 prop 会阻止用户更改输入。如果你指定了 value,但输入仍可编辑,则可能是你意外地将value 设置为 undefined 或 null。
ReactDOM.render(<input value="h" />, mountNode);
setTimeout(function() {
ReactDOM.render(<input value={null} />, mountNode);
}, 1000);
6、非受控组件
受控组件表单数据是由React组件来管理的。非受控组件表单数据由DOM节点处理。
1)使用ref来从DOM 节点中获取表单数据
class NameRefForm extends React.Component {
constructor(props) {
super(props)
this.handleSubmit = this.handleSubmit.bind(this)
this.input = React.createRef()
}
handleSubmit(event) {
console.log(this.input.current.value)
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
姓名:<input type="text" ref={this.input} />
</label>
<input type='submit' value="提交" />
</form>
)
}
}
ReactDOM.render(<NameRefForm />, document.getElementById('roo
2)默认值
在非受控组件赋予组件一个初始值可以使用defaultValue属性而不是value。
checkbox、radio支持defaultChecked;select、textarea支持defaultValue。
<input defaultValue="Roman" type="text" ref={this.input} />
3)文件input标签
<input type="file" />始终是非受控组件。文件input标签的value是只读的,值只能用户设置而不能通过代码控制。
使用ref实现提交表单获取文件信息:
class FileInput extends React.Component {
constructor(props) {
super(props);
this.file = React.createRef();
this.handleSubmit = this.handleSubmit.bind(this)
}
handleSubmit(event) {
console.log(this.file.current.files[0].name)
event.preventDefault()
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>file:
<input ref={this.file} type="file" />
</label>
<br />
<input type="submit" value="submit" />
</form>
)
}
}
ReactDOM.render(<FileInput />, document.getElementById('root')
其他
4、表单实现可以使用Formik
如果想寻找包含验证、追踪访问字段以及处理表单提交的完整解决方案,用 Formik 是不错的选择。然而,它也是建立在受控组件和管理 state 的基础之上 —— 所以不要忽视学习它们。