dangerouslySetInnerHTML
React写进{}内的东西,不允许被当作代码块解析,是为了防止xss攻击和代码注入
XSS(跨站脚本攻击,Cross-Site Scripting) 是一种常见的安全漏洞,攻击者通过注入恶意脚本到网页中,从而在用户浏览器中执行恶意代码。XSS 攻击通常发生在 Web 应用程序中,尤其是当用户输入的内容未经过滤或转义就直接显示在页面上时。
代码注入:恶意用户可以通过注入 HTML 或 JavaScript 代码,篡改页面内容或窃取用户数据
不过如果你想忽略跨站脚本攻击也是可以的
dangerouslySetInnerHTML 是React 中用于直接设置 HTML 内容的一个特殊属性。它的名字中的 "dangerously" 暗示了它的潜在风险:如果使用不当,可能会导致 XSS(跨站脚本攻击) 等安全问题
javascript
class App extends Component{
a=100
myref = React.createRef()//返回一个ref对象
state = {
list: [
{
id:1,text:'111'
},
{ id: 2, text: '222' },
{id:3,text:'333'}
]
}
render(){
return (
<div>
<input ref={this.myref} />
{/* //把ref绑定在input上 */}
<button onClick={this.handleClick}>add</button>
<ul>
{
this.state.list.map((item, index) => <li key={item.id}>
<span dangerouslySetInnerHTML={
{ __html: item.text }
}></span>
<button onClick={() => this.handleDel(index)}>delete</button></li>)
}
</ul>
<div className="hidden">暂无待办事项</div>
</div>
)
}
只有你很信任后端给的数据才可以这么写哦

或者是后端ajax返回的数据可以直接被渲染也可以这么用

影院案例
使用猫的眼睛的电影对外的API访问数据
javascript
constructor() {
super()
this.state = {
cinemaList: [],
bakcinemaList:[]
}
//用axios请求数据
axios.get('https://apis.netstart.cn/maoyan/index/moreCinemas?day=2021-11-12&offset=0&limit=20&districtId=-1&lineId=-1&hallType=-1&brandId=-1&serviceId=-1&areaId=-1&stationId=-1&item&updateShowDay=true&reqId=1636710166221&cityId=1&lat=23.135636443326355&lng=1').then(res => {
console.log(res)
this.setState({
cinemaList: res.data,
bakcinemaList: res.data
})
}).catch(err => {
console.log(err)
})
}

再用map将数组元素转化为标签进行渲染
javascript
{
this.state.cinemaList.map(item => <dl key={item.cinemaId}>
<dt>{ item.title}</dt>
<dd>{ item.location}</dd>
</dl>)
}

加上样式:
css
*{
margin: 0;
padding:0;
}
ul{
list-style: none;
display:flex;
position: fixed;
bottom:0px;
left:0px;
height: 50px;
line-height:50px;
width:100%;
background-color:white ;
}
ul li{
flex:1;
text-align: center;
}
.active{
color:red;
}
dl{
height:50px;
border-bottom:1px solid grey;
}
dl dt{
font-size: 20px;
}
dl dd{
font-size: 12px;
color: grey;
}
input{
width: 100%;
height: 30px;
line-height: 30px;
font-size: medium;
}
记得导入样式

当触发事件的元素和ref为一个元素时,可以利用事件对象e代替ref
所以在这个模糊搜索功能处,可以用target.value获取input输入框的值,然后使用filter筛选标题和地址满足要求的影院

很简单的小项目捏
javascript
import React from 'react'
import axios from 'axios'
class Cinema extends React.Component{
constructor() {
super()
this.state = {
cinemaList: [],
bakcinemaList:[]
}
//用axios请求数据
axios.get('https://apis.netstart.cn/maoyan/index/moreCinemas?day=2021-11-12&offset=0&limit=20&districtId=-1&lineId=-1&hallType=-1&brandId=-1&serviceId=-1&areaId=-1&stationId=-1&item&updateShowDay=true&reqId=1636710166221&cityId=1&lat=23.135636443326355&lng=1').then(res => {
console.log(res)
this.setState({
cinemaList: res.data,
bakcinemaList: res.data
})
}).catch(err => {
console.log(err)
})
}
//生命周期函数更适合发送ajax请求
render() {
return (
<div>
<input onChange={this.handleInput}/>
{
this.state.cinemaList.map(item => <dl key={item.cinemaId}>
<dt>{ item.title}</dt>
<dd>{ item.location}</dd>
</dl>)
}
</div>
)
}
handleInput = (event) => {
//感觉这里可以加一个防抖
console.log('input', event.target.value)
let newList = this.state.bakcinemaList.filter(item => item.title.includes(event.target.value)||item.location.includes(event.target.value))
this.setState({
cinemaList:newList
})
}
}
export default Cinema
setState在类组件里也同样是异步的
javascript
handleClick = () => {
this.setState({
count:this.state.count+1
})
console.log(this.state.count)
this.setState({
count:this.state.count+1
})
console.log(this.state.count)
this.setState({
count:this.state.count+1
})
console.log(this.state.count)
}
}
setState处在同步的环境中,异步更新状态,异步更新真实dom,一个事件循环彻底结束后,在下一轮宏任务中更新状态

在react18以前,setState在异步的情况下是同步的,不过现在React 18 及之后 :引入了自动批处理,即使在异步代码中,setState
也可能表现为异步。

useState
的更新既不是宏任务,也不是微任务,而是由 React 的调度机制管理。
setState是改变状态的,你怎么知道他什么时候改变完呢?
setState的第二个参数可以传回调函数,获取的状态是当前作用域内的state,也就是说:
javascript
handleClick2 = () => {
setTimeout(() => {
this.setState({
count:this.state.count+1
},()=> console.log('setState的第二个参数',this.state.count))
console.log('外部的state',this.state.count)
},0)
}
这个第二个参数里的回调函数,是在状态和dom都更新完以后才执行的

BetterScroll
让页面更平滑的滚动
我个人感觉这更像是对setState第二个参数的妙用,因为这个回调函数会在状态和dom都完成更新后再执行
例如BetterScroll本身可以更平滑的滚动,但是要想把数据全部包在wraper里,就要在dom树和状态都更新完以后,再创建wrapper实例:
javascript
import React, { Component } from "react"
import BetterScroll from 'better-scroll'
class App extends Component{
state={
list:[]
}
render(){
return (
<div>
<button onClick={()=>this.getData()}>click</button>
<div className='wrapper' style={{height:'200px',background:'yellow',overflow:'hidden'}}>
<ul className='content' >
{
this.state.list.map(item => <li key={item}>{item}</li>)
}
</ul>
</div>
</div>
)
}
getData() {
let list = [1, 2, 3, 4, 5, 6, 7, 8,9,10,11,12,13]
this.setState({
list:list
}, () => { new BetterScroll('.wrapper') })
}
}
export default App

把影院组件套到BetterScroll上来,一定要满足以下结构:
javascript
return (
<div>
<input onChange={this.handleInput}/>
<div className='wrapper' style={{height:'500px',background:'yellow',overflow:'hidden'}}>
<div className='content'>
{
this.state.cinemaList.map(item => <dl key={item.cinemaId}>
<dt>{ item.title}</dt>
<dd>{ item.location}</dd>
</dl>)
}
</div>
</div>
</div>
)
kerwin老师说可以不放在setState的第二个参数上也可以执行,我试了试react18不行哦
javascript
//用axios请求数据
axios.get('https://apis.netstart.cn/maoyan/index/moreCinemas?day=2021-11-12&offset=0&limit=20&districtId=-1&lineId=-1&hallType=-1&brandId=-1&serviceId=-1&areaId=-1&stationId=-1&item&updateShowDay=true&reqId=1636710166221&cityId=1&lat=23.135636443326355&lng=1').then(res => {
console.log(res)
this.setState({
cinemaList: res.data,
bakcinemaList: res.data
},()=>{new BetterScroll('.wrapper') })//貌似react18必须放在这个位置
}).catch(err => {
console.log(err)
})

状态是react里很重要的一种数据挂载的方式捏
属性
属性之于标签就像参数之于函数,属性是父组件传过来的:
javascript
import React, { Component } from "react"
import Navbar from "./base/Navbar"
class App extends Component{
render(){
return (
<div>
<div>
<h2>首页</h2>
<Navbar tittle='首页'/>
</div>
<div>
<h2>列表</h2>
<Navbar tittle='列表'/>
</div>
<div>
<h2>购物车</h2>
<Navbar tittle='购物车'/>
</div>
</div>
)
}
}
export default App
通过this.props获取属性

渲染到网页上去:
javascript
import React, { Component } from "react"
class Navbar extends Component{
state = {
//只能内部自己用,外部无法改变
}
//属性是父组件传来的,通过this.props获取
render() {
console.log(this.props)
let {tittle}=this.props
return (
<div>
Navbar-{tittle}
</div>
)
}
}
export default Navbar
这样我们就实现了对属性的键值对使用,在组件上通过key=value 写属性,通过this.props获取属性,这样组件的可复用性提高了。
还可以根据属性进行条件渲染
false和true要放在{}里才会被当作boolean处理,不然就是字符串,只要字符串不为空怎么判断都是true
props.js
javascript
import React, { Component } from "react"
import Navbar from "./base/Navbar"
class App extends Component{
render(){
return (
<div>
<div>
<h2>首页</h2>
<Navbar tittle='首页' leftshow={false}/>
</div>
<div>
<h2>列表</h2>
<Navbar tittle='列表' leftshow={true}/>
</div>
<div>
<h2>购物车</h2>
<Navbar tittle='购物车' leftshow={true}/>
</div>
</div>
)
}
}
export default App
index.js
javascript
import React, { Component } from "react"
class Navbar extends Component{
state = {
//只能内部自己用,外部无法改变
}
//属性是父组件传来的,通过this.props获取
render() {
console.log(this.props)
let {tittle,leftshow}=this.props
return (
<div>
{leftshow &&<button>返回</button>}
Navbar-{tittle}
<button>home</button>
</div>
)
}
}
export default Navbar
首页模块无法返回,所以不显示左按钮

验证属性
在接收属性的时候要做验证
类属性:定义在类本身上的属性,而不是类的实例上的属性。它们通常用于存储与类相关的共享数据或方法。在es7里为了区分类属性和对象属性,在前面加一个关键字static,所以类属性也叫静态属性
对象属性:定义在类的实例上的属性,每个实例都有自己独立的对象属性。要想访问对象属性,就必须先创建实例
javascript
class Test{
a = 1//对象属性
static a=100//类属性
}
let obj = new Test()
console.log('类属性',Test.a,'对象属性:',obj.a)

做好对属性封装的验证,才能知道你用的属性对不对:
javascript
import React, { Component } from "react"
import AugustTypes from 'prop-types'
console.log(AugustTypes)
class Navbar extends Component {
state = {
//只能内部自己用,外部无法改变
}
//属性是父组件传来的,通过this.props获取
render() {
console.log(this.props)
let { title, leftshow } = this.props
return (
<div>
{leftshow && <button>返回</button>}
Navbar-{title}
<button>home</button>
</div>
)
}
}
//类属性
Navbar.propTypes = {
title: AugustTypes.string,
leftshow:AugustTypes.bool
}
export default Navbar

添加默认属性
可以这样写在外面

也可以加上static关键字写在类里面:
javascript
import React, { Component } from "react"
import AugustTypes from 'prop-types'
console.log(AugustTypes)
class Navbar extends Component {
state = {
//只能内部自己用,外部无法改变
}
//类属性
static propTypes = {
title: AugustTypes.string,
leftshow:AugustTypes.bool
}
//默认属性
static defaultProps = {
leftshow:true
}
//属性是父组件传来的,通过this.props获取
render() {
console.log(this.props)
let { title, leftshow } = this.props
return (
<div>
{leftshow && <button>返回</button>}
Navbar-{title}
<button>home</button>
</div>
)
}
}
export default Navbar
属性注意事项
可以利用对象的解构赋值来添加属性,或者说属性也可以写成一个对象:
javascript
import React, { Component } from "react"
import Navbar from "./base/Navbar"
class App extends Component{
render() {
let obj = {
title: 'test',
leftshow:false
}
return (
<div>
<Navbar title={obj.title} leftshow={obj.leftshow}/>
<Navbar {...obj} />
</div>
)
}
}
export default App
效果是一样的

在函数式组件里props是函数的形参,在使用的时候解构就可以,之前学过这里就不写了
状态和属性的区别
相似点:都是纯js对象,都会触发render更新,都具有确定性(状态/属性相同,结果相同)
不同点:
属性能从父组件获取,状态不能
属性可以由父组件修改,状态不能
属性能在内部设置默认值,状态也可以,设置方式不一样
属性不在组件内部修改,状态要在组件内部修改
属性能设置子组件初始值,状态不可以
属性可以修改子组件的值,状态不可以
state 的主要作用是用于组件保存、控制、修改自己的可变状态。 state 在组件内部初始化,可以被 组件自身修改,而外部不能访问也不能修改。你可以认为 state 是一个局部的、只能被组件自身控制 的数据源。 state 中状态可以通过 this.setState 方法进行更新, setState 会导致组件的重新渲染。
props 的主要作用是让使用该组件的父组件可以传入参数来配置该组件。它是外部传进来的配置参 数,组件内部无法控制也无法修改。除非外部组件主动传入新的 props ,否则组件的 props 永远保持不变(算是一种单向数据流)。父子通信本身也是子向父发出请求,父再修改属性传给子,属性是只读的:子组件不能直接修改父组件传递的属性。
没有 state 的组件叫无状态组件(stateless component),设置了 state 的叫做有状态组件 (stateful component)。因为状态会带来管理的复杂性,我们尽量多地写无状态组件,尽量少地写有状态的组件。这样会降低代码维护的难度,也会在一定程度上增强组件的可复用性。
状态可以被当作属性值传递给子组件
