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

发布于:2025-02-22 ⋅ 阅读:(15) ⋅ 点赞:(0)

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

批量渲染

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

点标记写法

// 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

组件通信

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

组件组合

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属性传递子组件

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

另一种写法

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

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里,没办法独立使用

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

使用多组内容传递

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

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

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是这么做的:

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原生的:

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不一定是项目用的

类型验证就用我之前已经写过的这部分👇

// 添加 prop-types 验证
Welcome.propTypes = {
  count: PropTypes.number, // 验证 count 是数字(非必传)
  msg: PropTypes.string, // 验证 msg 是字符串(非必传)
}

浏览器莫名其妙不报错,可能还是react的版本问题

如果希望有个数据传过来,数字和字符串都是符合要求的类型,也就是筛出两种类型可以这么写:

// 添加 prop-types 验证
Welcome.propTypes = {
    count:PropTypes.oneOfType([
        PropTypes.string,
        PropTypes.number,
  ]),// 验证 count 是数字或字符串(非必传)
  msg: PropTypes.string, // 验证 msg 是字符串(非必传)
}

还可以有一些更复杂的限定,比如限定值是多少:

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:

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,所以不是纯函数组件

这样就是了

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渲染里起效

//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++ 只是修改了本地变量

对于对象来说也是这样,应该把新的对象写进对应方法,而不是直接修改

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

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