💻 React 基础篇(二)🏠专栏:React
👀个人主页:繁星学编程🍁
🧑个人简介:一个不断提高自我的平凡人🚀
🔊分享方向:目前主攻前端,其他知识也会阶段性分享🍀
👊格言:☀️没有走不通的路,只有不敢走的人!☀️
👉让我们一起进步,一起成为更好的自己!!!🎁
文章目录
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.props
return (
<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.props
return (
<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 官网
安装
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.props
return (
<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方式才能使用this
super()
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;
结束语:
希望对您有一点点帮助,如有错误欢迎小伙伴指正。
👍点赞:您的赞赏是我前进的动力!
⭐收藏:您的支持我是创作的源泉!
✍评论:您的建议是我改进的良药!
一起加油!!!💪💪💪