深入浅出 React 组件
React 组件介绍
- 组件是 React 的
一等公民
,使用 React 就是在用组件。
- 组件表示页面中的部分功能。
- 组合多个组件实现完整的页面功能。
- 特点:
- 可复用。
- 独立性。
- 可组合。
React 组件的两种创建方式
使用函数创建组件:
- 函数组件:使用 JS 的函数(或建瓯函数)创建的组件
- 约定一:函数名称必须以大写字母开头。
- 约定二:函数组件必须有返回值,表示该组件的结构。
- 如果返回值是 null ,表示不渲染任何内容。
function Hello () { return ( <div>这是我的第一个函数组件!</div> ) }
2.渲染函数组件:用函数名作为组件标签名。
- 组件标签可以是单标签也可以是双标签。
function Hello () { return ( <div>这是我的第一个函数组件!</div> ) } // 渲染 ReactDOM.render(<Hello />, document.querySelector('#root')
用箭头函数创建组件:
const Hello = () => <div>这是我的第一个函数组件</div> // 这个渲染的效果等同于上面的那个函数创建的组件。
3.总结:
- 使用 JS 中的函数创建的组件叫做:函数组件。
- 函数组件必须有返回值。
- 组件名称必须以大写字母开头,React 据此区分 组件 和 普通的 React 元素。
- 使用函数名作为组件标签名。
使用类创建组件:
- 类组件:使用 ES6 的 class 创建的组件。
- 约定一:类名称也必须以大写字母开头。
- 约定二:类组件应该继承 React.Component 父类,从而可以使用父类中提供的方法或属性。
- 约定三:类组件必须提供 render() 方法。
- 约定四:render() 方法必须有返回值,表示该组件的结构。
class Hello extends React.Component { render () { return <div>Hello Class Component!</div> } } ReactDOM.render(<Hello />, document.querySelector('#root')
抽离为独立 JS 文件
- 思考:项目中的组件多了以后,该如何组织这些组件呢?
- 选择一:将所有组件放在同一个 JS 文件中。
- 选择二:将每个组件放到单独的 JS 文件中。
- 组件作为一个独立的个体,一般都会放到一个单独的 JS 文件中。
- 抽离组件的步骤:
- 创建 Hello.js 文件。
- 在 Hello.js 中导入 React 。
- 创建组件(函数组件或类组件)。
- 在 Hello.js 中导出该组件。
- 在 index.js 中导入 Hello 组件。
- 渲染组件。
// Hello.js 文件 import React from 'react' class Hello extends React.Component { render () { return <div>Hello Class Component!</div> } } // 导出 Hello 组件 export default Hello
// index.js 文件 import Hello from './Hello' // 渲染导入的 Hello 组件 ReactDOM.render(<Hello />, document.querySelector('#root')
React 事件处理
事件绑定
- React 事件绑定语法与 DOM 事件语法相似。
- 语法:
on + 事件名称 = {事件处理程序}
- 比如:
onClick={() => {}}
3.注意:**React 事件采用驼峰命名法
,比如:onMouseEnter、onFocus 。
// 通过类创建组件 class App extends React.Component { handleClick() { console.log('单击事件触发了') } render() { return ( <button onClick={this.handleClick}>点我</button> ) } }
// 通过函数创建组件 function App () { function handleClick() { console.log('单击事件触发了') } return ( <button onClick={this.handleClick}>点我</button> ) }
事件对象
- 可以通过事件处理程序的参数获取到事件对象。
- React 中的事件对象叫做:
合成事件(对象)
。
- 合成事件:兼容所有浏览器,无需担心跨域浏览器兼容性问题。
function handleClick(e) { e.preventDefault() console.log('事件对象',e) } <a onClick={handleClick}>点我不会跳转页面</a>
4.具体使用:
function App() { function handleClick(e) { // 阻止浏览器的默认行为 e.preventDefault() console.log('事件对象',e) } return( <a href="www.baidu.com" onClick={handleClick}>点击页面不跳转</a> ) } // 渲染react元素 ReactDOM.render(<App />, document.querySelector('#root'))
有状态组件和无状态组件
- 函数组件又叫做无状态组件,类组件又叫做有状态组件。
- 状态(state)即数据。
- 函数组件没有自己的状态,只负责数据展示(静)。
- 类组件有自己的状态,负责更新 UI ,让页面“动”起来。
- 比如计数器案例中,点击按钮让数值加1.0和1就是不同时刻的状态,而由0变1就表示状态发生了变化。状态变化后,UI 也要相应的更新。React 中想要实现该功能,就要使用有状态组件来完成。
组件中的 state 和 setState
state 的基本使用
- 状态(state)即数据,是组件内部的私有数据,只能在组件内部使用。
- state 的值是对象,表示一个组件中可以有多个数据。
class App extends React.Component { constyuctor() { super() // 初始化 state this.state = { count: 0 } } // 简化写法(推荐) state = { count: 0 } render(){ return ( <div> <h1>计数器:{this.state.count}</h1> </div> ) } } ReactDOM.render(<App />, document.querySelector('#root'))
3.总结:
- 状态即数据。
- 状态是私有的,只能在组件内部使用。
- 通过
this.state
来获取状态。
setState() 修改状态
- 状态是可变的。
- 语法:
this.setState({要修改的数据})
- 注意:不要直接修改 state 中的值,这是错误的!!!
// 正确 this.setState({ count: this.state.count + 1 }) // 错误 this.state.count += 1
4.setState() 的作用:
- 修改 state 。
- 更新 UI 。
5.思想:数据驱动视图。
从 JSX 中抽离事件处理程序
- JSX 中掺杂过多 JS 逻辑代码,会显得非常混乱。
- 推荐:将逻辑抽离到单独的方法中,保证 JSX 结构清晰。
- 原因:事件处理程序中 this 的值为 undefined 。
- 希望:this 指向组件实例(render 方法中的 this 即为组件实例)
事件绑定 this 指向
箭头函数
- 利用箭头函数自身不绑定 this 的特点。
- render() 方法中的 this 为组件实例,可以获取到 setState() 。
class App extends React.Component { state = { count: 0 } onIncrement() { this.setState({ count: this.state.count + 1 }) } render() { // 箭头函数中的 this 指向外部环境,此处为:render() 方法。 return ( <div> <h1>总数:{this.state.count}</h1> <button onClick={() => this.onIncrement()}>+1</button> </div> ) } }
Function.prototype.bind()
- 利用 ES5 中的 bind 方法,将事件处理程序中的 this 与组件实例绑定到一起。
class App extends React.Component { constructor() { super() this.state = { count: 0 } this.onIncrement = this.onIncrement.bind(this) } onIncrement() { this.setState({ count: this.state.count + 1 }) } render() { // 箭头函数中的 this 指向外部环境,此处为:render() 方法。 return ( <div> <h1>总数:{this.state.count}</h1> <button onClick={this.onIncrement}>+1</button> </div> ) } }
class 的实例方法
- 利用箭头函数形式的 class 实例方法。
- 注意:该语法是实验性语法,但是,由于 babel 的存在可以直接调用。
class App extends React.Component { state = { count: 0 } onIncrement = () => { this.setState({ count: this.state.count + 1 }) } render() { return ( <div> <h1>总数:{this.state.count}</h1> <button onClick={this.onIncrement}>+1</button> </div> ) } }
总结
- 推荐:使用 class 的实例方法。
class Hello extends React.Component { onIncrement = () => { this.setState({...}) } }
2.箭头函数。
<button onClick={() => this.onIncrement()}>+1</button>
3.bind
constructor() { super() this.onIncrement = this.onIncrement.bind(this) }
表单处理
受控组件
- HTML 中的表单元素是可输入的,也就是有自己的可变状态。
- 而 React 中可变状态通常保存在 state 中,并且只能通过 setState() 方法来修改。
- React 将 state 与表单元素的 value 绑定到了一起,由 state 的值来控制表单元素的值。
<input type="text" value={this.state.txt} />
4.受控组件:其值受到 React 控制的表单元素。
5.步骤:
- 在 state 中添加一个状态,作为表单元素的 value 值(控制表单元素值的来源)。
state = { txt: '' }
<input type="text" value={this.state.txt} />
给表单元素绑定 change 事件,将表单元素的值设置为 state 的值(控制表单元素值的变化)。
<input type="text" value={this.state.txt} onChange={e => this.setState({ txt: e.target.value })} />
6.实例:
- 文本框
class App extends React.Component { state = { txt: '' } handleChange = e => { this.setState({ txt: e.target.value }) } render() { return ( <div> <input type="text" value={this.state.txt} onChange={this.handleChange} /> <h2>{this.state.txt}</h2> </div> ) } }
富文本框、下拉框
class App extends React.Component { state = { content: '' } handleContent = e => { this.setState({ content: e.target.value }) } render() { return ( <div> <input type="text" value={this.state.content} onChange={this.handleContent} /> <h2>{this.state.content}</h2> </div> ) } }
下拉框
class App extends React.Component { state = { city: 'sh' } handleCity = e => { this.setState({ city: e.target.value }) } render() { return ( <div> <select value={this.state.city} onChange={this.handleCity}> <option value="bj">北京</option> <option value="sh">上海</option> <option value="gz">广州</option> </select> <h2>{this.state.city}</h2> </div> ) } }
复选框
class App extends React.Component { state = { isChecked: true } handleChecked = e => { this.setState({ isChecked: e.target.checked }) } render() { return ( <div> <input type="checkbox" checked={this.state.isChecked} onChange={this.handleChecked} /> </div> ) } }
示例总结
- 文本框、富文本框、下拉框操作 value 属性。
- 复选框操作 checked 属性。
多表单元素优化
- 问题:每个表单元素都有一个单独的事件处理程序处理太繁琐。
- 优化:使用一个事件处理程序同时处理多个表单元素。
- 步骤:
- 给表单元素添加 name 属性,名称与 state 相同。
<input type="text" name="txt" value={this.state.txt} onChange={this.handleForm} />
根据表单元素类型获取对应值。
// 根据表单元素类型获取值 const value = target.type === 'checkbox' ? target.checked : target.value // 根据 name 设置对应 state this.setState({ [name]: value })
- 在 change 事件处理程序中通过 [name] 来修改对应的 state 。
- 实例:
class App extends React.Component { state = { txt: '', content: '', city: 'sh', isChecked: true } handleForm = e => { // 获取当前 DOM 对象 const target = e.target // 根据类型获取值 const value = target.type === 'checkbox' ? target.checked : target.value // 获取 name const name = target.name this.setState({ [name]: value }) } render() { return ( <div> <input name="txt" type="text" value={this.state.txt} onChange={this.handleForm} /> <br/> <h1>{this.state.txt}</h1> <textarea name="content" type="checkbox" checked={this.state.content} onChange={this.handleForm} /> <br/> <h1>{this.state.content}</h1> <select name="city" value={this.state.city} onChange={this.handleForm}> <option value="bj">北京</option> <option value="sh">上海</option> <option value="gz">广州</option> </select> <br/> <h1>{this.state.city}</h1> <input name="isChecked" type="checkbox" checked={this.state.isChecked} onChange={this.handleForm} /> <br/> <h1>{this.state.isChecked}</h1> </div> ) } }
非受控组件
- 使用步骤:
- 调用 React.createRef() 方法创建一个 ref 对象。
constructor() { super() this.txtRef = React.createRef() }
将创建好的 ref 对象添加到文本框中。
<input type="text" ref={this.txtRef} />
通过 ref 对象获取到文本框的值。
console.log(this.txtRef.current.value)
- 大多数情况下使用受控组件。
总结
- 组件的两种创建方式:函数组件和类组件。
- 无状态(函数)组件,负责静态结构展示。
- 有状态(类)组件,负责更新 UI ,让页面动起来。
- 绑定事件注意 this 指向。
- 推荐使用受控组件来处理表单。
- 完全利用 JS 语言的能力创建组件,这是 React 的思想。