千峰React:函数组件使用(2)

前面写了三千字没保存,恨!

批量渲染

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

相关推荐
崔庆才丨静觅5 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60616 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了6 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅6 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅6 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅7 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment7 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅7 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊7 小时前
jwt介绍
前端
爱敲代码的小鱼7 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax