面向组件编程
函数式组件
普通的
<script type="text/babel">
// 1、定义函数式组件
function MyComponent(){
return <h1>函数式组件的入门案例~</h1>;
}
ReactDOM.render(<MyComponent/>,document.getElementById("test"));
</script>
注意 :组件的名称首字母必须大写
带参数的
<script type="text/babel">
function GetDate(param){
return <h2>{param.data.toLocaleTimeString()}</h2>;
}
ReactDOM.render(<GetDate data={new Date()} />,document.getElementById("test"));
</script>
类式组件
复习类
<script>
class Person{
constructor(name,age){
this.name=name;
this.age = age;
}
speak(){
console.log(`我叫${this.name},今年${this.age}岁`);
}
}
const p1 = new Person('Tom',15);
const p2 = new Person('jack',21);
console.log(p1);
console.log(p2);
p1.speak();
p2.speak();
class Student extends Person{
constructor(name,age,grade){
super(name,age);
this.grade = grade;
}
speak(){
console.log(`我叫${this.name},今年${this.age}岁,在读${this.grade}年级`);
}
}
const s1 = new Student('张三',21,'大一');
console.log(s1);
s1.speak();
</script>
类式组件应用
<script type="text/babel">
// 创建一个类式组件,必须继承 React.Component 类
class MyClassComponent extends React.Component{
// render 必须是写的,必须有返回值
render(){
return <h1>类式组件入门案例</h1>;
}
}
// 渲染组件到页面
ReactDOM.render(<MyClassComponent />,document.getElementById("test"));
</script>
组件的分类
如果组件中有 状态 state 就是复杂组件,没有则是简单组件。
组件的实例的三大属性
函数组件没有实例,类式组件有实例
stat
注意 :
1、组件自定义的方法中 this 为 undefined,如何解决?
2、状态数据,不能直接修改或更新。需要通过 setState 方式去更新state 中的值。
<script type="text/babel">
// 创建组件
class Weather extends React.Component {
// 创建构造器
constructor(props){
super(props)
this.state = {isHot:true}
// 解决 类方法中 this 丢失问题
this.changeWeather = this.changeWeather.bind(this)
}
render(){
return <h2 onClick={this.changeWeather}>今天天气很{this.state.isHot ? '炎热' : '寒冷'}</h2>
}
changeWeather(){
// 类中的方法默认开了局部严格模式,所以 this 会丢失,this 的值为 undefined
// 解决办法 :在构造器中加一行代码 :this.changeHot = this.changeHot.bind(this) 就会将方法挂在示例自身上
//this.state.isHost = !this.state.isHot
// 状态(state)中的值不可以直接更改,上面是直接更改,需要通过一个内置的API去修改(setState)
const {isHot} = this.state;
this.setState({isHot:!isHot});
}
}
// 渲染到页面
ReactDOM.render(<Weather/>,document.getElementById("test"))
</script>
构造器中东西简写(推荐这样写)
以后类中的自定义方法都使用 : 赋值语句+箭头函数
<script type="text/babel">
class Weather extends React.Component {
state = {isHost : true}
render(){
return <h2 onClick={this.changeHot}>今天天气很{this.state.isHost ? '炎热':'寒冷'}</h2>;
}
// 普通函数还是会发生this丢失,这里只能使用 箭头函数才可以保证this不丢失
changeHot = () => {
const {isHost} = this.state;
this.setState({isHost:!isHost})
}
}
ReactDOM.render(<Weather />,document.getElementById("test"))
</script>
props
简单应用
注意 :props 是只读的,在组件内部不允许修改 props 里的值
需求 :
- 姓名必须指定,且为字符串
- 性别为字符串类型,如果性别没有指定,默认是男
- 年龄为数字类型,默认是18
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../js/react.development.js"></script>
<script src="../js/react-dom.development.js"></script>
<script src="../js/babel.min.js"></script>
<!-- 用于对组件标签属性进行限制 -->
<script src="../js/prop-types.js"></script>
</head>
<body>
<div id="test"></div>
<div id="test2"></div>
<script type="text/babel">
// 加载类组件
class Person extends React.Component{
render(){
// props 是只读的
// this.props.name = "test" // 此行代码会报错,因为 props 是只读的
return (
<ul>
<li>姓名 : {this.props.name}</li>
<li>年龄 : {this.props.age}</li>
<li>性别 : {this.props.sex}</li>
</ul>
);
}
}
// 对类式组件中所需要的属性做限制,这个时候需要新引入一个 js 文件../js/prop-types.js
Person.propTypes = {
name : PropTypes.string.isRequired, //名字非空且为字符串
sex : PropTypes.string, // 性别必须是字符串
age : PropTypes.number, // 年龄为数字类型
speak : PropTypes.func // 限制 speak 为函数
}
// 指定类式组件中属性默认值
Person.defaultProps = {
sex : "男",
age : 18
}
// <Person name="tom" age="21" sex="男"/> 这样参数是直接存放到 props 组件中
ReactDOM.render(<Person name="tom" age={21} sex="男" speak={speak}/>,document.getElementById("test"))
// 也可以封装成一个对象直接传参
const p ={name:"tom",age:21,sex:"男"};
// ...p 为展开对象,是babel和react的功能。与js 中的 {...p} 不是一回事
ReactDOM.render(<Person {...p}/>,document.getElementById("test2"))
function speak(){
console.log("speak");
}
</script>
</body>
</html>
简写方式(将属性的限制和默认值放入类中)(推荐这样写)
<script type="text/babel">
// 加载类组件
class Person extends React.Component{
// 对类式组件中所需要的属性做限制,这个时候需要新引入一个 js 文件../js/prop-types.js
propTypes = {
name : PropTypes.string.isRequired, //名字非空且为字符串
sex : PropTypes.string, // 性别必须是字符串
age : PropTypes.number, // 年龄为数字类型
speak : PropTypes.func // 限制 speak 为函数
}
// 指定类式组件中属性默认值
defaultProps = {
sex : "男",
age : 18
}
render(){
return (
<ul>
<li>姓名 : {this.props.name}</li>
<li>年龄 : {this.props.age}</li>
<li>性别 : {this.props.sex}</li>
</ul>
);
}
}
const p ={name:"tom",age:21,sex:"男"};
ReactDOM.render(<Person {...p}/>,document.getElementById("test2"))
</script>
函数式组件使用 props
<script type="text/babel">
// 加载类组件
function Person(props) {
console.log(props);
return (
<ul>
<li>姓名 : {props.name}</li>
<li>年龄 : {props.age}</li>
<li>性别 : {props.sex}</li>
</ul>
);
}
// 限制和默认值,在函数式组件也可以使用
Person.propTypes = {
name : PropTypes.string.isRequired, //名字非空且为字符串
sex : PropTypes.string, // 性别必须是字符串
age : PropTypes.number, // 年龄为数字类型
speak : PropTypes.func // 限制 speak 为函数
}
Person.defaultProps = {
sex : "男",
age : 18
}
const p ={name:"tom",age:21,sex:"男"};
ReactDOM.render(<Person {...p} />, document.getElementById("test"))
</script>
refs
案例为 :提示在文本中输入的值(两种方式 :点击按钮提示、失去焦点提示)
分类 :
- 字符串形式 refs
- 回调函数形式 refs (用的最多的)
- 官方 API :createRef (react 最推荐的)
字符串形式
不久将来将要废弃
<script type="text/babel">
class Demo extends React.Component {
render() {
return (
<div>
<input type="text" ref="input1" placeholder="点击按钮提示数据" />
<button onClick={this.showData}>点击提取左侧数据</button>
<input onBlur={this.showData2} ref="input2" type="text" placeholder="失去焦点提示数据" />
</div>
)
}
showData = () => {
// 拿到 dom 节点
const input1 = this.refs.input1;
// 获取值
const value = input1.value;
alert(value)
}
showData2 = () => {
// 拿到 dom 节点
const input2 = this.refs.input2;
// 获取值
const value = input2.value;
alert(value)
}
}
ReactDOM.render(<Demo />, document.getElementById("test"))
</script>
回调函数形式
<script type="text/babel">
class Demo extends React.Component {
render() {
return (
<div>
// 回调函数中的参数是代表节点本身,回调函数的意义是将该节点赋值给 示例 对象上
<input type="text" ref={c => this.input1 = c } placeholder="点击按钮提示数据" />
<button onClick={this.showData}>点击提取左侧数据</button>
<input onBlur={this.showData2} ref={c => this.input2 = c} type="text" placeholder="失去焦点提示数据" />
</div>
)
}
showData = () => {
// 拿到 dom 节点
const input1 = this.input1;
// 获取值
const value = input1.value;
alert(value)
}
showData2 = () => {
// 拿到 dom 节点
const input2 = this.input2;
// 获取值
const value = input2.value;
alert(value)
}
}
ReactDOM.render(<Demo />, document.getElementById("test"))
</script>
官方 API :createRef
<script type="text/babel">
class Demo extends React.Component {
/**
* React.createRef 调用后可以返回一个容器,该容器可以存储被ref所标识的节点
* 该容器是专人专用,一个容器只能存一个节点
*/
myRef = React.createRef()
myRef2 = React.createRef()
render() {
return (
// ref={this.myRef} 将该节点存储到 myRef 容器中
<div>
<input type="text" ref={this.myRef} placeholder="点击按钮提示数据" />
<button onClick={this.showData}>点击提取左侧数据</button>
<input onBlur={this.showData2} ref={this.myRef2} type="text" placeholder="失去焦点提示数据" />
</div>
)
}
showData = () => {
// 拿到 dom 节点
const input1 = this.myRef.current;
// 获取值
const value = input1.value;
alert(value)
}
showData2 = () => {
// 拿到 dom 节点
const input2 = this.myRef2.current;
// 获取值
const value = input2.value;
alert(value)
}
}
ReactDOM.render(<Demo />, document.getElementById("test"))
</script>
总结
- status 用来初始化值
- props 用来传递参数(类式组件和函数式组件都可以使用)
- ref 用来表示一个组件的标签,拿到对应的 dom 元素,相当于以前的 id
事件
当绑定事件的节点和要操作的节点是同一个节点的时候 ,事件可以用来解决 refs 过度使用的问题。。。
<script type="text/babel">
class Demo extends React.Component {
render() {
return (
<div>
<input onBlur={this.showData} type="text" placeholder="失去焦点提示数据" />
</div>
)
}
showData = (event) => {
alert(event.target.value)
}
}
ReactDOM.render(<Demo />, document.getElementById("test"))
</script>
收集表单数据
案例
需求: 定义一个包含表单的组件
输入用户名密码后, 点击登录提示输入信息
组件分类
- 非受控组件 :表单中的输入类的组件,都是现用现取。
- 受控组件 :输入数据时,将数据维护到状态中去,需要用的时候在从状态中取出来(双向绑定)。优点是省去了繁琐的 refs
非受控组件
<script type="text/babel">
class Login extends React.Component {
render() {
return (
<form onSubmit={this.handleSubmit}>
用户名 : <input ref={c => this.username = c} type="text" name="username" />
密码 : <input ref={c => this.password = c} type="password" name="password" />
<button>登陆</button>
</form>
)
}
handleSubmit = (event) => {
event.preventDefault() // 阻止表单提交,真实开发中可以在下面使用 ajax 发起请求
const {username,password} = this
alert(`用户名是 : ${username.value},密码是 : ${password.value}`)
}
}
ReactDOM.render(<Login />, document.getElementById("test"))
</script>
受控组件
<script type="text/babel">
class Login extends React.Component {
// 初始化状态
state = {
username:'',
password:''
}
render() {
return (
<form onSubmit={this.handleSubmit}>
用户名 : <input onChange={this.savaUsername} type="text" name="username" />
密码 : <input onChange={this.savePassword} type="password" name="password" />
<button>登陆</button>
</form>
)
}
savaUsername = (event) =>{
this.setState({username:event.target.value})
}
savePassword = (event) =>{
this.setState({password:event.target.value})
}
handleSubmit = (event) => {
event.preventDefault() // 阻止表单提交,真实开发中可以在下面使用 ajax 发起请求
const {username,password} = this.state
alert(`用户名是 : ${username},密码是 : ${password}`)
}
}
ReactDOM.render(<Login />, document.getElementById("test"))
</script>
高阶函数、函数柯里化
我们正常写方法的时候是 onChange={this.saveFormData} ,这样的话不写高阶函数,在组件加载的时候就会执行该方法
而使用了高阶函数之后就可以这样了onChange={this.saveFormData('username')}
高阶函数
如果一个函数符合下面两个规范中的任何一个,该函数就是高阶函数 :
- 若A函数,接收的参数是一个函数。
- 若A函数,调用的返回值是一个函数。
函数的柯里化
通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式
<script type="text/babel">
class Login extends React.Component {
// 初始化状态
state = {
username:'',
password:''
}
render() {
return (
<form onSubmit={this.handleSubmit}>
用户名 : <input onChange={this.saveFormData('username')} type="text" name="username" />
密码 : <input onChange={this.saveFormData('password')} type="password" name="password" />
<button>登陆</button>
</form>
)
}
// 高阶函数、函数的柯里化
saveFormData = (data) =>{
return (event)=>{
// 对象里面的变量 :[] 可以传入变量,如果不用 [] ,传入的是值
this.setState({[data]:event.target.value})
}
}
handleSubmit = (event) => {
event.preventDefault() // 阻止表单提交,真实开发中可以在下面使用 ajax 发起请求
const {username,password} = this.state
alert(`用户名是 : ${username},密码是 : ${password}`)
}
}
ReactDOM.render(<Login />, document.getElementById("test"))
</script>
不用柯里化
<script type="text/babel">
class Login extends React.Component {
// 初始化状态
state = {
username:'',
password:''
}
render() {
return (
<form onSubmit={this.handleSubmit}>
用户名 : <input onChange={event=>this.saveFormData('username',event)} type="text" name="username" />
密码 : <input onChange={event=>this.saveFormData('password',event)} type="password" name="password" />
<button>登陆</button>
</form>
)
}
saveFormData = (data,vevnt) =>{
// 对象里面的变量 :[] 可以传入变量,如果不用 [] ,传入的是值
this.setState({[data]:event.target.value})
}
handleSubmit = (event) => {
event.preventDefault() // 阻止表单提交,真实开发中可以在下面使用 ajax 发起请求
const {username,password} = this.state
alert(`用户名是 : ${username},密码是 : ${password}`)
}
}
ReactDOM.render(<Login />, document.getElementById("test"))
</script>
|