1.目标
能够使用函数创建组件
能够使用class创建组件
能够给React元素绑定事件
能够使用state和setState()
能够处理事件中的this指向问题
能够使用受控组件方式处理表单
2.目录
React组件介绍
React组件的两种创建方式
React事件处理
有状态组件和无状态组件
组件中的state和setState()
事件绑定this指向
表单处理
3.react组件介绍
组件是React的一等公民,使用React就是在用组件
组件表示页面中的部分功能
组合多个组件实现完整的页面功能
特点:可复用、独立、可组合
4.react组件的两种创建方式
A. 使用函数创建组件
B. 使用类创建组件
4.1 使用函数创建组件
A. 函数组件:使用JS的函数(或箭头函数)创建的组件
B. 约定1:函数名称必须以大写字母开头
C. 约定2:函数组件必须有返回值,表示该组件的结构
D. 如果返回值为null,表示不渲染任何内容
function Hello(){
return (
<div>这是我的第一个函数组件</div>
)
}
A. 渲染函数组件:用函数名作为组件标签名
B. 组件标签可以是单标签也可以是双标签
ReactDOM.render(<Hello></Hello>,document.getElementById('root'))
4.2 使用类创建组件
A. 类组件:使用ES6的class创建的组件
B. 约定1:类名称也必须以大写字母开头
C. 约定2:类组件必须继承React.Component父类,从而可以使用父类中提供的方法或属性
D. 约定3:类组件必须提供render()方法
E. 约定4:render()方法必须有返回值,表示该组件的结构
class Hello1 extends React.Component{
render(){
return (
<div>
我是一个类组件
</div>
)
}
}
ReactDOM.render(<Hello1 />,document.getElementById('root'))
4.3 抽离为单独JS文件
A. 思考:项目中的组件多了之后,该如何组织这些组件呢?
B. 选择一:将所有组件放在同一个JS文件中
C. 选择二:将每个组件放到单独的JS文件中
D. 组件作为一个独立的个体,一般都会放到一个单独的JS文件中
E. 抽离步骤:
\1. 创建Hello.js
\2. 在Hello.js中导入React
\3. 创建组件(函数或类)
\4. 在Hello.js中导出该组件
\5. 在index.js中导入Hello组件
\6. 渲染组件
5.React事件处理
\1. 事件绑定
\2. 事件对象
5.1 事件绑定
A. React事件绑定语法与DM事件语法相似
B. 语法:on+事件名称=[事件处理程序],比如:onClick={()=>{}}
C. 注意:React事件采取驼峰命名法,比如:onMouseEnter、onFocus
D. 在函数组件绑定事件:
Index.js(函数事件绑定)
// 函数组件事件绑定
function Hello() {function clickHandle() {console.log("函数事件触发了");}return <button onClick={clickHandle}>点击触发函数事件</button>;
}ReactDOM.createRoot(document.getElementById("root")).render(<Hello></Hello>);
index.js(类事件绑定)
// 类事件绑定
class Hello2 extends React.Component {clickHandle() {console.log("类事件触发了");}render() {return <button onClick={this.clickHandle}>点击触发类事件</button>;}
}ReactDOM.createRoot(document.getElementById("root")).render(<Hello2></Hello2>);
区别:
5.2 事件对象
A. 可以通过事件处理程序的参数获取到事件对象
B. React中的事件对象叫做:合成事件(对象)
C. 合成事件:兼容所有浏览器,无需担心跨浏览器兼容性问题
// 事件对象
class App extends React.Component {clickHandel(e) {// 阻止浏览器默认行为e.preventDefault();console.log("点击事件触发了");}render() {return (<a href="https://www.baidu.com" onClick={this.clickHandel}>百度</a>);}
}ReactDOM.createRoot(document.getElementById("root")).render(<App></App>);
6.有状态组件和无状态组件
A. 函数组件又叫做无状态组件,类组件又叫做有状态组件
B. 状态(state)即数据
C. 函数组件没有自己的状态,只负责数据展示(静)
D. 类组件有自己的状态,负责更新UI,让页面”动”起来
比如计算器案例中,点击按钮让数值加1。0和1就是不同时刻的状态,而由0变为1就表示状态发生了变
化。状态变化后,UI也要相应的更新。React中想要实现该功能,就要使用有状态组件来完成。
7.组件中的state和setState
A. state的基本使用
B. setState()修改状态
7.1 state的基本使用
A. 状态(state)即数据,是组件内部的私有数据,只能在组件内部使用
B. state的值是对象,表示一个组件中可以有多个数据
C. 状态即数据
D. 状态是私有的,只能在组件内部使用
E. 通过this.state来获取状态
// 有状态组件
class Hello3 extends React.Component {constructor() {super(); // 必须写}state = {count: 0,};render() {return (<div>有状态组件<h1>计数器:{this.state.count}</h1></div>);}
}ReactDOM.createRoot(document.getElementById("root")).render(<Hello3></Hello3>);
7.2 setState()修改状态
A. 状态是可变的
B. 语法:this.setState({要修改的数据})
C. 注意:不要直接修改state中的值,这是错误的!!!!
D. setState()作用:1.修改state 2.更新UI
E. 思想:数据驱动视图
// 有状态组件
class Hello3 extends React.Component {constructor() {super(); // 必须写}state = {count: 0,};render() {return (<div>有状态组件<h1>计数器:{this.state.count}</h1><buttononClick={() => {this.setState({ count: this.state.count + 1 });}}>+1</button></div>);}
}ReactDOM.createRoot(document.getElementById("root")).render(<Hello3></Hello3>);
7.3 从JSX中抽离事件处理程序
A. JSX中掺杂过多JS逻辑代码,会显得非常混乱
B. 推荐:将逻辑抽离到单据的方法中,保证JSX结构清晰
// 有状态组件
class Hello3 extends React.Component {constructor() {super(); // 必须写}state = {count: 0,};onIncrement() { console.log(this, "抽离出来的this");this.setState({count: this.state.count + 2,});}render() {return (<div>有状态组件<h1>计数器:{this.state.count}</h1><buttononClick={() => {console.log(this, "函数式事件触发的this");this.setState({ count: this.state.count + 1 });}}>+1</button><button onClick={this.onIncrement}>+2</button></div>);}
}ReactDOM.createRoot(document.getElementById("root")).render(<Hello3></Hello3>);
A. 原因:事件处理程序中this的值为undefined—抽离出来的方法用箭头函数可解决
B. 希望:this指向组件实例(render方法中的this即为组件实例)
把state放在,构造函数里面
8.事件绑定this指向
A. 抽离出的事件处理函数使用箭头函数
B. Function.prototype.bind() 【在构造函数中使用.bind(this)来绑定this上下文】
C. 在render方法中使用箭头函数来调用onIncrement,但这样每次渲染都会创建一个新的函数,可能会影响性能
8.1 箭头函数
A. 利用箭头函数自身不绑定this的特点
B. Render()方法中的this为组件实例,可以获取到setState()
8.2 在构造函数中使用.bind(this)来绑定this上下文–Function.prototype.bind()
8.3 在render方法中使用箭头函数来调用onIncrement,但这样每次渲染都会创建一个新的函数,可能会影响性能
<button onClick={() => this.onIncrement()}>+2</button>
9.表单处理
\1. 受控组件
\2. 非受控组件(DOM方式)
9.1 受控组件
A. HTML中的表单元素是可输入的,也就是有自己的可变状态
B. 而,React中可变状态通常保存在state,并且只能通过setState()方法来修改
C.React将state与表单元素值value绑定到一起,由state的值来控制表单元素的值
D. 受控组件:其值受到React控制的表单元素
E. 步骤
\1. 在state中添加一个状态,作为表单元素的value值(控制表单元素值的来源)
\2. 给表单元素绑定change事件,将表单元素的值设置为state的值(控制表单元素值的变化)
class Hello4 extends React.Component {constructor() {super();this.state = {txt: "默认值",};}inputHandle = (e) => {this.setState({txt: e.target.value,});};render() {return (<div>文本的值:{this.state.txt}<br /><inputtype="text"value={this.state.txt}onChange={this.inputHandle}></input></div>);}
}ReactDOM.createRoot(document.getElementById("root")).render(<Hello4></Hello4>);
示例:
A.文本框、富文本框、下拉框
B.复选框
class Hello4 extends React.Component {constructor() {super();this.state = {txt: "默认值",content: "",city: "zj",isChecked: true,};}handleChange1 = (e) => {this.setState({txt: e.target.value,});};handleChange2 = (e) => {this.setState({content: e.target.value,});};handleChange3 = (e) => {this.setState({city: e.target.value,});};handleChange4 = (e) => {console.log(e, "e4");this.setState({isChecked: e.target.checked,});};render() {return (<div>{/* 输入框 */}文本的值:{this.state.txt}<inputtype="text"value={this.state.txt}onChange={this.handleChange1}></input><br />{/* 文本框 */}文本框的值:{this.state.content}<textareavalue={this.state.content}onChange={this.handleChange2}></textarea><br />{/* 下拉框 */}下拉框的值:{this.state.city}<select value={this.state.city} onChange={this.handleChange3}><option value="zj">浙江</option><option value="js">江苏</option><option value="sh">上海</option><option value="bj">北京</option></select><br />{/* 复选框 */}复选框的值:{this.state.isChecked}<inputtype="checkbox"checked={this.state.isChecked}onChange={this.handleChange4}></input></div>);}
}ReactDOM.createRoot(document.getElementById("root")).render(<Hello4></Hello4>);
9.1.1 多表单元素优化
A. 问题:每个表单元素都有一个单独的事件处理程序处理太繁琐
B. 优化:使用一个事件处理程序同时处理多个表单元素
9.1.2 多表单元素优化步骤
A. 给表单元素添加name属性,名称与state相同
B. 根据表单元素类型获取对应值
C. 在change事件处理程序中通过[name]来修改对应的state
class Hello4 extends React.Component {constructor() {super();this.state = {txt: "默认值",content: "",city: "zj",isChecked: true,};}handleForm = (e) => {const value =e.target.type === "checkbox" ? e.target.checked : e.target.value;this.setState({[e.target.name]: value,});};render() {return (<div>{/* 输入框 */}文本的值:{this.state.txt}<inputtype="text"name="txt"value={this.state.txt}onChange={this.handleForm}></input><br />{/* 文本框 */}文本框的值:{this.state.content}<textareaname="content"value={this.state.content}onChange={this.handleForm}></textarea><br />{/* 下拉框 */}下拉框的值:{this.state.city}<select value={this.state.city} name="city" onChange={this.handleForm}><option value="zj">浙江</option><option value="js">江苏</option><option value="sh">上海</option><option value="bj">北京</option></select><br />{/* 复选框 */}复选框的值:{this.state.isChecked}<inputtype="checkbox"name="isChecked"checked={this.state.isChecked}onChange={this.handleForm}></input></div>);}
}ReactDOM.createRoot(document.getElementById("root")).render(<Hello4></Hello4>);
9.2 非受控组件
A. 说明:借助于ref,使用原生DOM方法来获取表单元素值
B. ref的作用:获取DOM或组件
C. 使用步骤:
- 调用React.createRef()方法创建一个ref对象
- 将创建好的ref对象添加到文本框中
- 通过ref对象获取到文本框的值
class Hello6 extends React.Component {constructor() {super();// 创建Refthis.textRef = React.createRef();}getText = () => {console.log(this.textRef);console.log(this.textRef.current.value);};render() {return (<div><input type="text" ref={this.textRef}></input><button onClick={this.getText}>获取文本框的值</button></div>);}
}
ReactDOM.createRoot(document.getElementById("root")).render(<Hello6></Hello6>);
10.react组件基础-案例评论列表
index.js
class Comment extends React.Component {constructor() {super();// 初始化值this.state = {userName: "",userContent: "",comments: [{ id: 1, name: "周可可", content: "大家好,我是周可可~~" },{ id: 2, name: "周嫑嫑", content: "每没有人能欺负周可可!!!" },{ id: 3, name: "周深", content: "大家好,我是周深~~" },{ id: 4, name: "周浅", content: "大家好,我是周浅~~" },],};}// 数据展示renderList = () => {return this.state.comments.length == 0 ? (<div>暂无评论,快去发布吧~</div>) : (<div><ul className="contentDetailBox">{this.state.comments.map((item) => (<li key={item.id}>评论人:{item.name}<br />评论内容:{item.content}</li>))}</ul></div>);};// 数据响应式changeForm = (e) => {const { name, value } = e.target;this.setState({[name]: value,});};// 添加addComment = () => {let { userName, userContent, comments } = this.state;// 非空校验if (userName.trim() == "" || userContent.trim() == "") {return alert("用户名或评论内容不能为空!");}const newComments = [{id: comments.length > 0 ? comments.length + 1 : 1,name: userName,content: userContent,},...comments,];console.log(newComments, "newComments");this.setState({ comments: newComments });// 清空数据this.state.userName = "";this.state.userContent = "";};render() {const { userName, userContent } = this.state;return (<div><div className="appp"><h1>评论</h1><div><inputname="userName"placeholder="请输入评论人"className="userIpt"value={userName}onChange={this.changeForm}></input></div><br /><div style={{ marginBottom: "10px" }}><textareaname="userContent"placeholder="请输入评论内容"rows={10}cols={50}className="contentTextarea"value={userContent}onChange={this.changeForm}></textarea></div><button onClick={this.addComment}>发布评论</button><div><h2 style={{ textAlign: "center" }}>评论详情</h2>{this.renderList()}</div></div></div>);}
}ReactDOM.createRoot(document.getElementById("root")).render(<Comment></Comment>
);
index.css
.appp {width: 35%;height: 700px;border: 1px solid gray;padding-left: 50px;
}.appp h1 {text-align: center;
}.userIpt {width: 40%;
}.contentDetailBox {height: 300px;overflow-y: scroll;
}