💻 React 基础篇(二)🏠专栏:React
👀个人主页:繁星学编程🍁
🧑个人简介:一个不断提高自我的平凡人🚀
🔊分享方向:目前主攻前端,其他知识也会阶段性分享🍀
👊格言:☀️没有走不通的路,只有不敢走的人!☀️
👉让我们一起进步,一起成为更好的自己!!!🎁
文章目录
- React 基础知识篇(二)
- style 样式
- 注释
- className 类名
- 不同的条件添加不同的类名
- (1) 使用三目运算
- (2) 使用第三方包实现
- classnames
- styled-components
- props
- (1) 使用
- (2) 默认值
- (3) 校验props接收传递值的数据类型
- 条件渲染
- (一) 三目运算符
- (1) 变量三目
- (2) style三目
- (3) class三目
- (二) 短路运算符
- 循环渲染
- 插槽
- (1) 使用
- (2) **具名插槽**
- state
- 定义state的两种方式
- setState
- 解析state中的标签
- 事件的传参与this的指向
- this的指向
- 事件传参
- 受控组件和非受控组件
- 表单输入绑定
- 非受控组件
- 状态提升
- 类组件中
- 函数组件中
React 基础知识篇(二)
style 样式
类组件和函数组件添加样式的方式是完全一样的
注意点:
- 添加样式的时候,要使用两个大括号{{}},外面是jsx的括号(表示将{}中的值变为js表达式),里面是对象的括号
return <div style={{color:'red'}}>style</div>
- 如果有连字符的,要写成小驼峰的语法
return <div style={{color:'red', fontSize:"20px"}}>style</div>
- 如果说单位是px的,可以省略单位,并且写成数字
注:并不是所有样式属性的px单位都可以省略(如lineHeight原本就不可以省略,省略后代表的是字体小的倍数)
return <div style={{color:'red',fontSize:20,height:'50px',lineHeight:2}}>style</div>
}
注释
jsx中的注释会被{}包裹,因为注释也被默认当作是js表达式
class About extends React.Component {render() {return <>{/* <div>nihao</div> */}</>}
}
className 类名
通过添加className类名修改样式
.test1 {color: red;
}
.test2 {background-color: aqua;
}
import React from 'react'
// 1. 引入css文件
import './class.css'
const testClass = ()=>{return <><div className='test1 test2'>通过class修改样式</div></>
}
export default testClass
注:在标签中不能用class属性要用className
,因为jsx中使用了类,防止起冲突。
还有一种标签上的属性名在jsx中发生了改变(label标签上的for属性改为htmlFor,防止与for循环起冲突)
<label htmlFor="fos">年龄</label>
<input type="text" id='fos' placeholder='请输入你的年龄'/>
不同的条件添加不同的类名
(1) 使用三目运算
class.css文件
.red {color: red;
}.green {color: green;
}
App.jsx根组件
import React from 'react'
// 1. 引入css文件
import './class.css'
// 2. 设置变量
const red = false;
const App = () => {return (<><h2>不同条件添加不同样式</h2>{/* 通常情况下,可以使用三目来解决问题 */}<p className={red ? "red" : "green"}>hello react!!</p></>);
};
export default App
(2) 使用第三方包实现
classnames
官网
classnames
安装
npm install classnames
yarn add classnames
import { Component } from 'react';
import classNames from 'classnames/bind';
import styles from './class.css';let cx = classNames.bind(styles);
let show = true;class changeColor extends Component {render () {let className = cx({colorRed: show,colorGreen: show,});return <div className={className}>通过第三包classNames修改样式</div>}
};
export default changeColor
styled-components
官网
styled-components
安装
npm install styled-components
yarn add styled-components
使用
import React from 'react'
import styled from 'styled-components';
// 方式一:
const Title = styled.h 1`font-size: 1.2em;text-align: center;color: palevioletred;
`;
// 方式二:进行函数传参
const Title1 = styled.h2`/* 还可以使用自定义属性进行props的传参 */font-size: ${(props) => props.size + "px"};color: ${(props) => props.color};
`;const testClass = ()=>{return <><Title className={Title}>通过class修改样式</Title><Title1 size='30'>h2</Title1><Title1 color='green'>h2-2</Title1></>
}
export default testClass
安装vscode中的插件:vscode-styled-components
(可以在styled.xx后的字符串中进行标签提示)
props
(1) 使用
props是父组件传递给子组件的自定义属性
特点
- props 具有响应式
- 遵循 单向数据流
- 子组件 不能直接修改props的值
注:
- 类组件通过this.props获取父组件传递的值
- 函数组件通过函数参数props获取父组件传递的值
import React, { Component } from 'react'
// 类组件通过this.props获取父组件传递的值
class Child1 extends Component {render() {const { name, age } = this.propsreturn (<div>child1 - {name} - {age}</div>);}
}
// 函数组件通过函数参数props获取父组件传递的值
const Child2 = ({ count }) => {return <><div>child2 - {count}</div></>
}class App extends Component {state = { name:'zs'}render() {return <><div>props传参</div><Child1 age={10} name={this.state.name} /><Child2 count={100} /></>}componentDidMount() {setTimeout(() => {this.setState({name: "lisi",});}, 2000);}
}export default App
(2) 默认值
import React, { Component } from 'react'class Child1 extends Component {// 类组件设置默认值的方式一static defaultProps = {name:'ww',age: 18,sex:'男'}render() {const { name, age, sex } = this.propsreturn (<div>child1 - {name} - {age} - {sex}</div>);}
}
// 类组件设置默认值的方式二
// Child1.defaultProps = {
// name:'ww',
// age: 18,
// sex:'男'
// }const Child2 = ({ count, sex }) => {return <><div>child2 - {count} - {sex}</div></>
}
// 函数组件设置默认值的方式
Child2.defaultProps = {count: 18,sex:'女'
}class App extends Component {state = { name:'zs'}render() {return <><div>props传参</div><Child1 name={this.state.name} age={"19"}/><Child2 count={100} /></>}
}export default App
(3) 校验props接收传递值的数据类型
使用第三方包:prop-types 官网
prop-types
安装
npm install --save prop-types
yarn add prop-types --save
常用的支持检测的数据类型
optionalArray: PropTypes.array,optionalBigInt: PropTypes.bigint,optionalBool: PropTypes.bool,optionalFunc: PropTypes.func,optionalNumber: PropTypes.number,optionalObject: PropTypes.object,optionalString: PropTypes.string,optionalSymbol: PropTypes.symbol,optionalEnum: PropTypes.oneOf(['News', 'Photos']),optionalUnion: PropTypes.oneOfType([PropTypes.string,PropTypes.number,PropTypes.instanceOf(Message)]),requiredFunc: PropTypes.func.isRequired,requiredAny: PropTypes.any.isRequired,
使用
import React, { Component } from 'react'
// 1.直接解构
import {string,number,oneOfType} from 'prop-types';class Child1 extends Component {// 类组件检测数据类型的方式一static propTypes = {name:string,age:oneOfType([string,number]),}render() {const { name, age,sex } = this.propsreturn (<div>child1 - {name} - {age} - {sex}</div>);}
}
// 类组件检测数据类型的方式二
// Child1.propTypes = {
// name:string,
// age:number
// }const Child2 = ({ count,sex }) => {return <><div>child2 - {count} - {sex}</div></>
}
// 函数组件检测数据类型的方式
Child2.propTypes = {count:number
}class App extends Component {state = { name:'zs'}render() {return <><div>props传参</div><Child1 name={this.state.name} age={"19"}/><Child2 count={100} /></>}
}export default App
条件渲染
(一) 三目运算符
(1) 变量三目
通过state中的变量控制标签的显示与隐藏(相当于Vue中的v-if)
class TestState extends Component {constructor(){super()this.state = {show: false}}render() {const {show} = this.state;return (<>{/* 变量三目运算 */}{show ? <p>显示1</p>:null}</>);}
}
(2) style三目
通过state中的变量控制标签样式切换来控制标签的显示与隐藏(相当于Vue中的v-show)
class TestState extends Component {constructor(){super()this.state = {show: false}}render() {const {show} = this.state;return (<>{/* style三目运算 */}<p style={{display : show?"block":"none"}}>显示3</p></>);}
}
(3) class三目
通过state中的变量控制标签不同类名的样式切换来控制标签的显示与隐藏(相当于Vue中的v-show)
class TestState extends Component {constructor(){super()this.state = {show: false}}render() {const {show} = this.state;return (<>{/* class三目运算(通过切换类名实现) */}<p className={show?'show':'noshow'}>显示4</p></>);}
}
// 样式文件中:
.show {display: block;
}
.noshow {display: none;
}
(二) 短路运算符
通过短路运算符(&&)来控制标签的显示与隐藏(Vue中的v-if)
class TestState extends Component {constructor(){super()this.state = {show: false}}render() {const {show} = this.state;return (<>{/* 短路运算 */} {show && <p>显示2</p>}</>);}
}
循环渲染
通过数组的map方法的映射来进行标签内容的循环渲染
class TestState extends Component {constructor(){super()this.state = {list: [1,2,3,4,5]}}render() {const {show} = this.state;return (<>{/* 循环渲染 */}<ul>{list.map(item =><li key={item}>{item}</li>)}</ul></>);}
}
插槽
(1) 使用
父组件(在父组件的子组件标签内容中直接写要传递的内容(也可以是标签))
import React, { Component } from 'react'class App extends Component {render() {return <><div>React 插槽</div><Child1><h1>h1</h1></Child1><Child2><span>span1</span></Child2></>}
}
export default App
类组件接收(使用this.props中的children)
import React, { Component } from 'react'
// 类组件使用this.props中的children进行接收父组件的子组件上的标签
class Child1 extends Component {render() {return (<><div>子类组件 - {this.props.children}</div></>);}
}
函数组件接收(使用函数形参children)
import React from 'react'
// 函数组件使用函数形参children进行接收父组件的子组件上的标签
const Child2 = (props) => {return <><div>子函数组件 - {props.children}</div></>
}
(2) 具名插槽
数组形式
可以在父组件传递多个内容时使用数组形式,子组件中使用数组的索引让其使用在不同的位置
- 父组件
import React, { Component } from 'react'class App extends Component {render() {return <><div>React 插槽</div><Child1><h1>h1</h1><h2>h2</h2></Child1><Child2><span>span1</span><span>span2</span></Child2></>}
}
export default App
- 子类组件
class Child1 extends Component {render() {return (<><div>{this.props.children[0]} - 子类组件 - {this.props.children[1]}</div></>);}
}
- 子函数组件
const Child2 = (props) => {return <><div>{props.children[0]} - 子函数组件 - {props.children[1]}</div></>
}
对象形式
可以在父组件传递多个内容时使用对象形式,子组件中使用数组的索引让其使用在不同的位置
- 父组件
import React, { Component } from 'react'class App extends Component {render() {return <><div>React 插槽</div><Child1>{{h1: <h1>h1</h1>,h2: <h2>h2</h2>}}</Child1><Child2>{{span1: <span>children1</span>,span2: <span>children2</span>}}</Child2></>}
}
export default App
- 子类组件
class Child1 extends Component {render() {return (<><div>{this.props.children.h1} - 子函数组件 - {this.props.children.h2}</div></>);}
}
- 子函数组件
const Child2 = (props) => {return <><div>{props.children.span1} - 子函数组件 - {props.children.span2}</div></>
}
state
相当于Vue中的data(响应式数据) 只有类组件可以定义自己的状态(state)
定义state的两种方式
使用构造函数的构造器进行定义
class TestState extends Component {// constructor当类实例化的时候被调用constructor(){// 在constructor里面需要先调用super方式才能使用thissuper()this.state = {name:'zs'}}render() {// 方式一:// return (// <>// <div>count:{this.state.count}</div>// </>// );// 方式二:解构const {count,name} = this.state;return (<><div>count:{count}</div><div>name:{name}</div></>);}
}
ES7 属性初始化器
class TestState extends Component {state = { count:10};render() {const {count,name} = this.state;return (<><div>count:{count}</div><div>name:{name}</div>{/* 添加属性的时候,如果使用了变量({}),不能用引号 */}<p title={this.state.title}>react</p></>);}
}
jsx中添加属性:(如果使用了变量({}),不能用引号)
<p title={this.state.title}>react</p>
setState
不能直接修改state的值,直接修改没有响应式
this.state.count = 10
setState里面有两个参数
- 第一个参数:对象或函数
- 第二个参数:回调函数
第一个参数可以是一个对象,这个对象会和state对象合并
this.setState({count:this.state.count+1,name:'ls'
})
当连续修改同一个变量的值,由于setState是异步的会同时执行,所以使用对象形式无法实现+1+1
this.setState({count:this.state.count+1,name:'ls'
})
this.setState({count:this.state.count+1,name:'ls'
})
第一个参数也可以是一个函数
如果一次的改动要基于前一次的值,用函数的写法(场景:上拉加载)
this.setState((prevState)=>{return{ count:prevState.count+1}
})
this.setState((prevState)=>{return{ count:prevState.count+1}
})
如果想要省略大括号和return,需要加小括号
this.setState((prevState)=>({count:prevState.count+1
}))
// 此时打印,输出的是修改前的值
console.log(this.state.count);// 修改前的值
第二个参数:可选的回调函数,会在数据改变之后调用
this.setState({count:this.state.count+1,name:'ls'
},()=>{console.log(this.state.count);
})
实现:在数组list中添加一项:4
this.setState((prevState)=>({// 方式一:利用数组的concat方法// list:prevState.list.concat(4)// 方式二:利用拓展运算符list:[...prevState.list,4]
}))
console.log(this.state.count);
// 仅限于复杂数据类型
// 方式三:直接修改list,然后在合并
this.state.list.push(4)
this.setState({})
注:
- 18版本之后所有的setState都是异步的
- 18版本之前(定时器、原生js是同步的)、合成事件和生命周期是异步的
解析state中的标签
类似于Vue中的v-html
state = { content:'<h1>v-html</h1>'
}
<div dangerouslySetInnerHTML={{__html:this.state.content}}></div>
事件的传参与this的指向
合成事件:事件名称要写成小驼峰(onClick)
<button onClick={this.handleClick}>btn</button>
this的指向
在类组件中直接写普通函数当事件处理函数的时候会出现this的指向不对的问题
改变this的指向的方式:
直接内联地使用 this.xxx.bind(this)(不推荐)
bind函数会产生一个新函数,并且新函数会存在于内存中,会导致内存占用太高
<button onClick={this.handleClick.bind(this)}>btn</button>
在constructor构造器里面使用 this.xxx = this.xxx.bind(this); (比较推荐,官网在用)
将bind产生的新函数存下来了,不会造成内存增加的问题
constructor() {super()this.handleClick = this.handleClick.bind(this)
}
使用箭头函数 (推荐)
箭头函数的this指向父级,既方便又不会增加内存
handleClick = () => {console.log(this.state.count);
}
事件传参
事件后面不要加括号,函数会自执行
<button onClick={this.handleClick()}>btn</button>
事件传参的方式:
内联地使用 this.xxx.bind(this, 2)
bind可以改变this指向。会产生一个新函数,bind的第二个参数会作为新函数的第一个参数 bind函数是不会自动调用的
不推荐,会使得内存增加
class App extends Component {state = {count: 1}handleClick(n) {console.log(this.state.count, n);}render() {return (<><div>事件</div><button onClick={this.handleClick.bind(this, 2)}>btn</button></>);}
}
内联地使用 onClick={() => this.xxx(2)}
不是特别推荐, 用的最多的方式 每一个箭头函数都会指向不同的内存,也会使内存增加
class App extends Component {state = {count: 1}handleClick = (n,e) => {console.log(this.state.count, n,e);}render() {return (<><div>事件</div><button onClick={(e) => this.handleClick(2,e)}>btn</button> </>);}
}
使用柯里化函数
class App extends Component {state = {count: 1}handleClick = (n) => (e) => {console.log(this.state.count, n ,e);}render() {return (<><div>事件</div><button onClick={this.handleClick(2)}>btn</button>);}
}
fn = (n) => () => {
console.log(n);
};
理论上最推荐的方式
受控组件和非受控组件
表单输入绑定
它们都是特指表单控件
- 受控组件让我们自己来控制的表单
- 只要input有value属性和onChange事件,那么就是受控组件
import React, { Component } from 'react'class App extends Component {state = {iptvalue: 'zs'}changeIpt = (e) => {this.setState(() => {return {iptvalue: e.target.value}})}render() {return (<><h2>表单绑定</h2><input type="text" value={this.state.iptvalue} onChange={this.changeIpt} /><p>{this.state.iptvalue}</p></>);}
}export default App;
非受控组件
使用createRef去创建内存,获取dom节点,使用变量指向该内存,然后绑定到input上
非受控组件的默认值要用defaultValue
<input type="text" defaultValue='非受控组件' />
import React, { Component, createRef } from 'react'class App extends Component {constructor() {super()this.ipt = createRef()}state = {textValue: 'zs'}chageIpt = () => {console.log(this.ipt);}render() {return (<><h1>非受控组件</h1>{/* 非受控组件的默认值要用defaultValue */}<input type="text" ref={this.ipt} defaultValue='非受控组件' /><button onClick={this.chageIpt}>btn</button></>);}
}export default App;
使用场景:文件上传框(只读,不能去修改信息)
状态提升
就是兄弟组件间传参
实现方式:先使用子传父,在利用父传子实现兄弟组件间的传参
- 父传子:在父组件的子组件标签上添加自定义属性,然后:
如果是类组件则通过this.props.属性名进行接收
如果是函数组件则通过函数参数props.属性名进行接收 - 子传父:在父组件的子组件标签上添加自定义事件,然后:
如果是类组件则通过this.props.自定义事件名调用父组件中的事件,并进行参数传递
如果是函数组件通过函数参数props.自定义事件名调用父组件中的事件,并进行参数传递
类组件中
传值的子组件1
import React, { Component } from "react";class Child1 extends Component {
state = {count: 10,
};render() {return <div>child1组件</div>;
}componentDidMount() {this.props.onGetcount(this.state.count);
}
}export default Child1;
父组件
import React, { Component } from "react";import Child1 from "./Child1";
import Child2 from "./Child2";class App extends Component {
state = {num: null,
};fn = (c) => {// console.log("fn", c);this.setState({num: c,});
};render() {return (<><h2>状态提升</h2><Child1 onGetcount={this.fn} /><Child2 num={this.state.num} /></>);
}
}export default App;
接收的子组件2
import React, { Component } from "react";class Child2 extends Component {
render() {return <div>child2组件 - {this.props.num}</div>;
}
}export default Child2;
函数组件中
传值的子组件1
import React, { useState } from 'react'const Child1 = (props) => {const [age] = useState(20)const handleClick = () => {props.onGetNum(age)}return (<><h3>子组件1</h3><span>C1: {age}</span><button onClick={handleClick}>将age传递给父组件</button></>);
}export default Child1;
父组件
import React, { useState } from 'react'
import Child1 from './Child1';
import Child2 from './Child2';
const App = () => {const [count, setCount] = useState(0)const fn = (n) => {setCount(n)}return (<><h3>父组件</h3><span>Fcount: {count}</span><Child1 onGetNum={fn} /><Child2 count={count} /></>);
}export default App;
接收的子组件2
import React from 'react'const Child2 = (props) => {return (<><h3>子组件2</h3><span>{props.count}</span></>);
}export default Child2;
结束语:
希望对您有一点点帮助,如有错误欢迎小伙伴指正。
👍点赞:您的赞赏是我前进的动力!
⭐收藏:您的支持我是创作的源泉!
✍评论:您的建议是我改进的良药!
一起加油!!!💪💪💪