前面写了三千字没保存,恨!
批量渲染

javascript
function App() {
const list = [
{id:0,text:'aaaa'},
{id:1,text:'bbbb'},
{id:2,text:'cccc'}
]
// for (let i = 0; i < list.length; i++) {
// list[i] = <li>{list[i]}</li>
// }
return (
<div>
<ul>
{list.map((item) => <li key={item.id}>{item.text}</li>)}
</ul>
</div>
)
}
export default App
点标记写法

javascript
// function Welcome() {
// return <div>hello Welcome</div>
// }
// function App() {
// return (
// <div>
// <Welcome />
// <Welcome></Welcome>
// </div>
// )
// }
// export default App
const Qf = () => {
return <div>hello Qf</div>
}
Qf.Welcome = () => {
return <div>hello Welcome</div>
}
function App() {
return (
<div>
<Qf.Welcome />
<Qf.Welcome></Qf.Welcome>
</div>
)
}
export default App
组件通信

javascript
import PropTypes from 'prop-types'
//子组件
function Welcome({ count, msg,isShow }) {
return (
<div>
hello Welcome,{count},{msg},{isShow+''}
</div>
)
}
// 添加 prop-types 验证
Welcome.propTypes = {
count: PropTypes.string.isRequired, // 验证 count 是字符串并且是必传的
msg: PropTypes.string.isRequired, // 验证 msg 是字符串并且是必传的
isShow: PropTypes.string.isRequired, // 验证isShow是字符串并且是必传的
}
//父组件
function App() {
const count='我是个变量'
return (
<div>
<Welcome count={count} msg='hi react' isShow />
</div>
)
}
export default App
组件组合

javascript
import PropTypes from 'prop-types'
//子组件
function Welcome() {
return <div>hello Welcome</div>
}
function Head() {
return <div>hello Head</div>
}
// 添加 prop-types 验证
Welcome.propTypes = {
count: PropTypes.string.isRequired, // 验证 count 是字符串并且是必传的
msg: PropTypes.string.isRequired, // 验证 msg 是字符串并且是必传的
isShow: PropTypes.string.isRequired, // 验证isShow是字符串并且是必传的
}
//父组件
function App() {
return (
<div>
hello App
<Welcome>
<Head />
</Welcome>
</div>
)
}
export default App
可以看见这个Head被包在Welcome里 是渲染不出来的

使用props的children属性传递子组件
javascript
import PropTypes from 'prop-types'
function Head() {
return <div>hello Head</div>
}
function Welcome({ children }) {
return (
<div>
hello Welcome
{children}
</div>
)
}
// 添加 prop-types 验证
Welcome.propTypes = {
count: PropTypes.string.isRequired, // 验证 count 是字符串并且是必传的
children: PropTypes.string.isRequired,
}
//父组件
function App() {
return (
<div>
hello App
<Welcome>
<Head />
</Welcome>
</div>
)
}
export default App

另一种写法
javascript
import PropTypes from 'prop-types'
function Head({ count }) {
return <div>hello Head,{count}</div>
}
function Welcome() {
const count = 456
return (
<div>
hello Welcome
<Head count={count} />
</div>
)
}
// 为 Head 组件添加 prop-types 验证
Head.propTypes = {
count: PropTypes.number.isRequired, // 验证 count 是数字并且是必传的
}
// 添加 prop-types 验证
Welcome.propTypes = {
}
//父组件
function App() {
return (
<div>
hello App
<Welcome>
<Head />
</Welcome>
</div>
)
}
export default App

javascript
import PropTypes from 'prop-types'
// 为 Head 组件添加 prop-types 验证
Head.propTypes = {
count: PropTypes.number.isRequired, // 验证 count 是数字并且是必传的
}
// 添加 prop-types 验证
Welcome.propTypes = {
count: PropTypes.number.isRequired, // 验证 Welcome 组件的 count 是必传的
}
function Head({ count }) {
//接收count
return <div>hello Head,{count}</div>
}
function Welcome() {
const count = 456
return (
<div>
hello Welcome
<Head count={count} />
{/* {挂载count} */}
</div>
)
}
//父组件
function App() {
const count = 123
return (
<div>
hello App
<Welcome count={count} />
</div>
)
}
export default App
如何分别传递多组内容
所有的内容放到children里,没办法独立使用
javascript
import PropTypes from 'prop-types'
function Welcome({ children }) {
return (
<div>
{children}
hello Welcome
{children}
</div>
)
}
// 添加 prop-types 验证
Welcome.propTypes = {
count: PropTypes.string.isRequired, // 验证 count 是字符串并且是必传的
children: PropTypes.string.isRequired,
}
//父组件
function App() {
return (
<div>
hello App
<Welcome>
<div>aaaaaaaaaaaa</div>
<div>bbbbbbbbbbbb</div>
</Welcome>
</div>
)
}
export default App

使用多组内容传递
javascript
import PropTypes from 'prop-types'
function Welcome({ bottom, top }) {
return (
<div>
{top}
hello Welcome
{bottom}
</div>
)
}
// 添加 prop-types 验证
Welcome.propTypes = {
count: PropTypes.string.isRequired, // 验证 count 是字符串并且是必传的
children: PropTypes.string.isRequired,
top: PropTypes.string.isRequired,
bottom: PropTypes.string.isRequired,
}
//父组件
function App() {
return (
<div>
hello App
<Welcome
top={<div>aaaaaaaaaaaa</div>}
bottom={<div>bbbbbbbbbbbb</div>}
></Welcome>
</div>
)
}
export default App

通信的数据如何添加默认值

javascript
import PropTypes from 'prop-types'
function Welcome({ count, msg }) {
return (
<div>
hello Welcome,{count},{msg}
</div>
)
}
// 添加 prop-types 验证
Welcome.propTypes = {
count: PropTypes.string.isRequired, // 验证 count 是字符串并且是必传的
children: PropTypes.string.isRequired,
msg: PropTypes.string.isRequired,
}
//父组件
function App() {
return (
<div>
hello App
{/* { 传递参数的情况} */}
<Welcome count={123} msg='hello react' />
{/* { 不传递参数的情况} */}
<Welcome />
</div>
)
}
export default App
如果有接收参数的位置,但是不传递参数,就会这样👇

为了解决这个问题,就提供了默认参数
ES6是这么做的:
javascript
import PropTypes from 'prop-types'
// function Head() {
// return <div>hello Head</div>
// }
function Welcome({ count='我是count的默认值', msg='我是msg的默认值'}) {
return (
<div>
hello Welcome,{count},{msg}
</div>
)
}
// 添加 prop-types 验证
Welcome.propTypes = {
count: PropTypes.string.isRequired, // 验证 count 是字符串并且是必传的
children: PropTypes.string.isRequired,
msg: PropTypes.string.isRequired,
}
//父组件
function App() {
return (
<div>
hello App
{/* { 传递参数的情况} */}
<Welcome count={123} msg='hello react' />
{/* { 不传递参数的情况} */}
<Welcome />
</div>
)
}
export default App
也可以使用React原生的:
javascript
import PropTypes from 'prop-types'
function Welcome({ count,msg}) {
return (
<div>
hello Welcome,{count},{msg}
</div>
)
}
// 添加 prop-types 验证
Welcome.propTypes = {
count: PropTypes.number, // 验证 count 是数字(非必传)
msg: PropTypes.string, // 验证 msg 是字符串(非必传)
}
Welcome.defaultProps = {
count: 0,
msg:'我是默认值'
}
//父组件
function App() {
return (
<div>
hello App
{/* { 传递参数的情况} */}
<Welcome count={123} msg='hello react' />
{/* { 不传递参数的情况} */}
<Welcome />
</div>
)
}
export default App
但是我失败了

为什么?因为:
我是19,别管了就用es6吧

通信数据如何限定类型

一般更推荐ts,但是ts不一定是项目用的
类型验证就用我之前已经写过的这部分👇
javascript
// 添加 prop-types 验证
Welcome.propTypes = {
count: PropTypes.number, // 验证 count 是数字(非必传)
msg: PropTypes.string, // 验证 msg 是字符串(非必传)
}
浏览器莫名其妙不报错,可能还是react的版本问题

如果希望有个数据传过来,数字和字符串都是符合要求的类型,也就是筛出两种类型可以这么写:
javascript
// 添加 prop-types 验证
Welcome.propTypes = {
count:PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
]),// 验证 count 是数字或字符串(非必传)
msg: PropTypes.string, // 验证 msg 是字符串(非必传)
}
还可以有一些更复杂的限定,比如限定值是多少:
javascript
import PropTypes from 'prop-types'
function Welcome({ count, msg }) {
return (
<div>
hello Welcome,{count},{msg}
</div>
)
}
// 添加 prop-types 验证
Welcome.propTypes = {
count: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), // 验证 count 是数字或字符串(非必传)
msg: PropTypes.string, // 验证 msg 是字符串(非必传)
type: PropTypes.oneOf(['primary', 'success', 'error']),
}
Welcome.defaultProps = {
count: 0,
msg: '我是默认值',
}
//父组件
function App() {
return (
<div>
hello App
{/* { 正确的情况} */}
<Welcome count={123} msg='hello react' type='我是错的' />
</div>
)
}
export default App
这里按理来说应该会报错,但是并没有!!!

传值其实也可以传jsx:
javascript
import PropTypes from 'prop-types'
function Welcome({ count, msg }) {
return (
<div>
hello Welcome,{count},{msg}
</div>
)
}
// 添加 prop-types 验证
Welcome.propTypes = {
count: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), // 验证 count 是数字或字符串(非必传)
msg: PropTypes.string, // 验证 msg 是字符串(非必传)
type: PropTypes.oneOf(['primary', 'success', 'error']),
icon: PropTypes.element//针对jsx的类型,element
}
Welcome.defaultProps = {
count: 0,
msg: '我是默认值',
}
//父组件
function App() {
return (
<div>
hello App
{/* { 正确的情况} */}
<Welcome
count={123}
msg='hello react'
type='primary'
icon={<div className='icon-close'></div>}
/>
</div>
)
}
export default App
组件必须是个纯函数

react的严格模式会检测我们当前的组件是不是纯函数
纯函数组件是指:
组件的输出(渲染结果)完全由输入(props 和 state)决定。
组件没有副作用(例如直接修改 DOM、发起网络请求、使用
setTimeout
等)。组件在相同的 props 和 state 下,总是返回相同的渲染结果
两次结果一样
在严格模式下,React 会 故意调用两次 函数组件(包括其渲染逻辑),如果组件是纯函数,两次调用的结果应该完全相同。
比如++操作,如果把对count的声明写函数外面,那么就不是一个纯函数:
预期来说count=2,最后等于3是因为React故意调用两次函数组件,两次的结果一次为2,一次为3,所以不是纯函数组件

这样就是了
javascript
function App() {
let count = 1
count++
console.log(count)
return <div>{count}</div>
}
export default App
两次结果相同

当然,不开严格模式的话就没有这种情况啦,一般建议开启
相同的输入和输出
意思就是给函数传入相同的参数,就应该传出相同的输出
这是一个纯函数,传入2都是4

这不是纯函数,传入2得到的输出不一定相同

纯函数可以保证我们的组件更健壮

组件的状态和useState
useState基础使用
useState 是一个 React Hook(函数),它允许我们向组件添加一个状态变量(State), 从而控制影响组件的渲染结果
状态变量是 React 组件内部的一个特殊变量,用于存储组件的数据,当状态变量的值发生变化时,React 会自动重新渲染组件,以反映最新的数据
状态变量和普通JS变量不同的是,状态变量一旦发生变化组件的视图UI也会跟着变化(数据驱动视图)
useState是React里的方法,返回的是个数组,数组里有两项:[状态变量,修改状态变量的方法]
只有用这个专门的修改状态变量的方法修改变量,才会在ui渲染里起效
javascript
//useState实现一个计数器按钮
import { useState } from 'react'
function App() {
// const handleClick = (name,e) => console.log('button被点击了name:',name,'e:',e)
//1、调用useState添加一个状态变量
const [count, setCount] = useState(0)
//2、点击事件回调
const handleClick = () => { setCount(count+1) }
return (
<div className="App">
<button onClick={handleClick}>{count}</button>
</div>
);
}
export default App;
count++
会直接修改 count
的值,而 React 的状态变量是不可变的(Immutable),不能直接修改。

那我就要问了:const handleClick = () => { setCount(count++) }这么写不也是通过setState 函数更新状态吗?为什么不行?

count++
会先返回当前的值,再进行自增操作,而 React 的 setState
需要传入新的值,而不是修改原值。
那++count呢?++count
会先对 count
进行自增操作,然后返回自增后的值。这个操作会直接修改当前 count
的值,而 React 需要的是通过 setCount
来触发状态的更新,这就相当于状态变量的修改是你自己改的,不是通过对应的方法改的
正确的写法是应该直接传入 count + 1
,而不是 count++
,因为 count++
只是修改了本地变量

对于对象来说也是这样,应该把新的对象写进对应方法,而不是直接修改
javascript
import { useState } from 'react'
function App () {
let [count, setCount] = useState(0)
const handleClick = () => {
// 直接修改 无法引发视图更新
// count++
// console.log(count)
setCount(count + 1)
}
// 修改对象状态
const [form, setForm] = useState({ name: 'jack' })
const changeForm = () => {
// 错误写法:直接修改
// form.name = 'john'
// 正确写法:setFrom 传入一个全新的对象
setForm({
...form,
name: 'john'
})
}
return (
<div>
<button onClick={handleClick}>{count}</button>
<button onClick={changeForm}>修改form{form.name}</button>
</div>
)
}
export default App

状态如何改变视图

先看这段代码
javascript
function App() {
let count = 0
const increment = () => {
count++ // 修改普通变量
console.log(count) // 值会变化,但不会触发重新渲染
}
console.log(1234567)
return (
<div>
<p>{count}</p>
<button onClick={increment}>Increment</button>
</div>
)
}
export default App
点击按钮时,count
的值会增加,但页面不会重新渲染,因此用户看不到变化。
状态变量会被记忆上一次的值,每次渲染函数返回的内容不一样,所以在察觉变化的时候React会重新调用渲染函数,并且改变count,所以每次结果都不一样)

普通变量的变化不会通知 React,所以React 不会调用渲染函数,所以不会重新渲染jsx。

顺便:Mujica第八集拍出来其实是为了缩短中国人寿命降低生产力,是日本人为了打败中国人投放的精神类武器,是日本成为东亚第一强国的邪恶计划最核心的一步