【React自学笔记06】React18函数式组件()

React18函数式组件

  • 基于React18新特性
  • 在原有的React基础上,补充函数式组件的知识点及用法

一、React事件

  • 在React中事件需要通过元素的属性来设置
  • 和原生JS不同,在React中事件的属性需要使用驼峰命名法:

onclick -> onClick

onchange -> onChange

  • 属性值不能直接执行代码,而是需要一个回调函数,事件触发时执行:

    onclick=”alert(123)”

    onClick={()=>{alert(123)}}

    onClick={clickHandler} 不要加括号,加了括号代表调用函数,我们需要的是事件触发才调用

  • onclick=”alert(123)”
  • onClick={()=>{alert(123)}}
  • onClick={clickHandler} 不要加括号,加了括号代表调用函数,我们需要的是事件触发才调用
  • 在React中,无法通过在clickHandler函数中return false取消默认行为

    event.preventDefault(); // 取消默认行为

    event.stopPropagation(); // 取消事件的冒泡

  • event.preventDefault(); // 取消默认行为
  • event.stopPropagation(); // 取消事件的冒泡
  • 事件对象event

    React事件中同样会传递事件对象,可以在响应函数中定义参数来接收事件对象

    React中的事件对象同样不是原生的事件对象,是经过React包装后的事件对象

    由于对象进行过包装,所以使用过程中我们无需再去考虑兼容性问题

  • React事件中同样会传递事件对象,可以在响应函数中定义参数来接收事件对象
  • React中的事件对象同样不是原生的事件对象,是经过React包装后的事件对象
  • 由于对象进行过包装,所以使用过程中我们无需再去考虑兼容性问题

二、Props

基础用法

在组件中如果将数据全部写死,将会导致组件无法动态设置,不具有使用价值

我们希望组件数据可以由外部设置,在组件间,父组件可以通过props(属性)向子组件传递数据

父组件中可以直接在子组件标签中设置属性,作为传递的参数值

  • 在函数组件中,属性props就相当于是函数的参数,可以通过参数来访问
  • 可以在函数子组件的形参中定义一个props,props指向的是一个对象
  • 它包含了父组件中传递的所有参数

注意:props只读不能修改

//Logs.js
import LogItem from './LogItem/logItem.js'
const Logs = () => {
  // 模拟一组从服务器中拿回来的数据
  const logsData = [
    {
      id: '001',
      date: new Date(2021, 1, 20, 18, 30),
      desc: '学习九阳神功',
      time: 30
    },
    {
      id: '002',
      date: new Date(2022, 2, 10, 12, 30),
      desc: '学习降龙十八掌',
      time: 20
    }
  ];
  return (
    <div className='logs'>{
      logsData.map((item) => {
        // return <LogItem key={item.id} date={item.date} desc={item.desc} time={item.time} />
        return <LogItem {...item} />
      })
    }
    </div >
  )
}
export default Logs
//LogItem.js
import MyDate from './MyDate/MyDate.js'

import './LogItem.css'
const LogItem = (props) => {
  return (
    <div className='item'>
      <MyDate date={props.date}/>
      <div className='content'>
        <h2 className="desc">{props.desc}</h2>
        <div className="time">{props.time}分钟</div>
      </div>
    </div>
  )
}
export default LogItem
import './MyDate.css'
const MyDate = (props) => {
  const month = props.date.toLocaleString('zh-CN', { month: 'long' })
  const day = props.date.getDate()
  return (
    <div className='date'>
      <div className='month'>{month}</div>
      <div className='day'>{day}</div>
    </div>
  )
}
export default MyDate

传递标签体

怎么把标签体传给card:props.className

//Card.js
import './Card.css'
const Card=(props)=>{
  return (
    <div className={`${props.className} card`}>{props.children}</div>
  )
}
export default Card

三、State

在React中,当组件渲染完毕后(先执行),再修改组件中的变量(后执行),不会使组件重新渲染;要使得组件可以受到变量的影响,必须在变量修改后对组件进行重新渲染;这里我们就需要一个特殊变量,当这个变量被修改时,组件会自动重新渲染。

state相当于一个变量,只是这个变量在React中进行了注册,React会监控这个变量的变化,当state发生变化时,会自动触发组件的重新渲染,使得我们的修改可以在页面中呈现出来。

注意:props是传给别人用的,state是对当前组件自身的属性(state是可变的,只属于当前属性)

定义步骤:

  • 在函数组件中,我们需要通过钩子函数,获取state;
  • 使用钩子 useState() 来创建state;
  • 导入包:import {useState} from “react”;

    useState()需要一个值作为参数,这个值就是state的初始值

    该函数会返回一个数组

    数组中第一个元素,是初始值,初始值只用来显示数据,直接修改不会触发组件的重新渲染

    数组中的第二个元素,是一个函数,通常会命名为setXxx,这个函数用来修改state,调用其修改state后会触发组件的重新渲染(重新调用render),并且使用函数中的值作为新的state值

  • useState()需要一个值作为参数,这个值就是state的初始值

    该函数会返回一个数组

    数组中第一个元素,是初始值,初始值只用来显示数据,直接修改不会触发组件的重新渲染

    数组中的第二个元素,是一个函数,通常会命名为setXxx,这个函数用来修改state,调用其修改state后会触发组件的重新渲染(重新调用render),并且使用函数中的值作为新的state值

  • 该函数会返回一个数组
  • 数组中第一个元素,是初始值,初始值只用来显示数据,直接修改不会触发组件的重新渲染
  • 数组中的第二个元素,是一个函数,通常会命名为setXxx,这个函数用来修改state,调用其修改state后会触发组件的重新渲染(重新调用render),并且使用函数中的值作为新的state值
import { useState } from "react";
import './counter.css'
const Counter = () => {
  const [counter, setCounter] = useState(1);
  const addHandler = () => {
    setCounter(counter + 1)
  }
  const minusHandler = () => {
    setCounter(counter - 1)
  }
  return (
    <div className="counter">
      <h2>{counter}</h2>
      <button onClick={addHandler}>+</button>
      <button onClick={minusHandler}>-</button>
    </div>
  )
}
export default Counter

总结:

state实际就是一个被React管理的变量;当我们通过setState()修改变量的值时,会触发组件的自动重新渲染;只有state值发生变化时,组件才会重新渲染

当state的值是一个对象时,修改时是使用新的对象去替换已有对象

  • setUser({…user, name: ‘猪八戒’});
  • const newUser = Object.assign({}, user);
    newUser.name = ‘猪八戒’;
    setUser(newUser);

如果直接修改旧的state对象,由于对象还是那个对象,所以不会生效

  • user.name = ‘猪八戒’;
  • setUser(user);

当通过setState去修改一个state时,并不表示修改当前的state,counter的初始值并没有改变;它修改的是组件下一次渲染时state值

setState()会触发组件的重新渲染,它是异步的;调完setState组件并不是立刻调用render函数进行渲染,而是先挂载到队列中,直到代码所有都执行完成再执行队列

所以当调用setState()需要用旧state的值时,一定要注意,有可能出现计算错误的情况;为了避免这种情况,可以通过为setState()传递回调函数的形式来修改state值

    const addHandler = () => {
        setTimeout(() => {
            setCounter((prevCounter)=>{
                /*
                *   setState()中回调函数的返回值将会成为新的state值
                *       回调函数执行时,React会将最新的state值作为参数传递
                * */
                return prevCounter + 1;
            });
            // setCounter(prevState => prevState + 1);
        }, 1000);
    };

只要用到之前的值就传回调

四、Ref

useRef()返回的就是一个普通的JS对象 {current:undefined},所以我们直接创建一个js对象,也可以代替useRef()

const h1Ref = {current:null};

const h1Ref = useRef(); // 创建一个容器

区别:

  • 我们创建的对象,组件每次重新渲染都会创建一个新对象

  • useRef()创建的对象,可以确保每次渲染获取到的都是同一个对象

  • 当需要一个对象不会因为组件的重新渲染而改变时,就可以使用useRef()

获取原生的DOM对象步骤

  • 1.创建一个存储DOM对象的容器

    使用 useRef() 钩子函数

    ① React中的钩子函数只能用于函数组件或自定义钩子

    ② 钩子函数只能直接在函数组件中调用

  • 使用 useRef() 钩子函数

    ① React中的钩子函数只能用于函数组件或自定义钩子

    ② 钩子函数只能直接在函数组件中调用

  • ① React中的钩子函数只能用于函数组件或自定义钩子
  • ② 钩子函数只能直接在函数组件中调用
  • 2.将容器设置为想要获取DOM对象元素的ref属性

    React会自动将当前元素的DOM对象,设置为容器current属性

  • React会自动将当前元素的DOM对象,设置为容器current属性
  • 代码:
    < h1 id="header" ref={h1Ref}>我是标题{count}
    h1Ref.current.innerText = ‘嘻嘻!’;

五、受控/非受控组件

非受控组件:监听表单项的变化

  • 创建三个变量,用来存储表单中的数据
  • 创建一个响应函数,监听相关数据的变化

    函数的参数为e:当前触发事件的对象,事件对象中保存了当前事件触发时的所有信息

    event.target 执行的是触发事件的对象(DOM对象)

  • 函数的参数为e:当前触发事件的对象,事件对象中保存了当前事件触发时的所有信息
  • event.target 执行的是触发事件的对象(DOM对象)
  • 在React中,通常表单不需要自行提交,而是要通过React提交

    取消表单的默认行为

    获取表单项中的数据

    将数据拼装为一个对象

  • 取消表单的默认行为
  • 获取表单项中的数据
  • 将数据拼装为一个对象
//LogsForm.js
import Card from "../UI/Card"
import './LogsForm.css'
const LogsForm = () => {
  let inputDate = ''
  let inputDesc = ''
  let inputTime = 0
  const dateChangeHandler = (e) => {
    inputDate = e.target.value
  }
  const descChangeHandler = (e) => {
    inputDesc = e.target.value
  }
  const timeChangeHandler = (e) => {
    inputTime = e.target.value
  }
  const formSubmitHandler=(e)=>{
    e.preventDefault()
    const newLog={
      date:new Date(),
      desc: inputDesc,
      time: +inputTime
    }
  }
  return (
    <Card className="logs-form">
      <form onSubmit={formSubmitHandler}>
        <div className="form-item">
          <label htmlFor="date">日期</label>
          <input id="date" type="date" onChange={dateChangeHandler} />
        </div>
        <div className="form-item">
          <label htmlFor="desc">内容</label>
          <input id="desc" type="text" onChange={descChangeHandler} />
        </div>
        <div className="form-item">
          <label htmlFor="time">时长</label>
          <input id="time" type="number" onChange={timeChangeHandler} />
        </div>
        <div className="form-btn">
          <button >添加</button>
        </div>
      </form>
    </Card>
  )
}
export default LogsForm

受控组件(双向绑定)

将表单中的数据存储到state中,然后将state设置为表单项value值,当表单项发生变化,state会随之变化,反之,state发生变化,表单项也会跟着改变

state提升,提升到共用组件里

//app.js
import LogsForm from './Components/LogsForm/LogsForm'
import Logs from './Components/Logs/Logs'
import Counter from './Components/Counter/Counter.js'
import { useState } from 'react';
import './app.css'
const App = () => {
  // 模拟一组从服务器中拿回来的数据
  const [logsData, setLogsData] = useState([
    {
      id: '001',
      date: new Date(2021, 1, 20, 18, 30),
      desc: '学习九阳神功',
      time: 30
    },
    {
      id: '002',
      date: new Date(2022, 2, 10, 12, 30),
      desc: '学习降龙十八掌',
      time: 20
    }
  ])
  const saveLogHandler = (newLog) => {
    let id = Date.now() + ''
    newLog = { id, ...newLog }
    setLogsData([...logsData, newLog])
  }
  return (
    <div className='app'>
      <LogsForm onSaveLog={saveLogHandler} />
      <Logs logsData={logsData} />
      <Counter />
    </div>
  )
}
export default App
//Logs.js
props.onSaveLog(newLog)
————————

React18函数式组件

  • 基于React18新特性
  • 在原有的React基础上,补充函数式组件的知识点及用法

一、React事件

  • 在React中事件需要通过元素的属性来设置
  • 和原生JS不同,在React中事件的属性需要使用驼峰命名法:

onclick -> onClick

onchange -> onChange

  • 属性值不能直接执行代码,而是需要一个回调函数,事件触发时执行:

    onclick=”alert(123)”

    onClick={()=>{alert(123)}}

    onClick={clickHandler} 不要加括号,加了括号代表调用函数,我们需要的是事件触发才调用

  • onclick=”alert(123)”
  • onClick={()=>{alert(123)}}
  • onClick={clickHandler} 不要加括号,加了括号代表调用函数,我们需要的是事件触发才调用
  • 在React中,无法通过在clickHandler函数中return false取消默认行为

    event.preventDefault(); // 取消默认行为

    event.stopPropagation(); // 取消事件的冒泡

  • event.preventDefault(); // 取消默认行为
  • event.stopPropagation(); // 取消事件的冒泡
  • 事件对象event

    React事件中同样会传递事件对象,可以在响应函数中定义参数来接收事件对象

    React中的事件对象同样不是原生的事件对象,是经过React包装后的事件对象

    由于对象进行过包装,所以使用过程中我们无需再去考虑兼容性问题

  • React事件中同样会传递事件对象,可以在响应函数中定义参数来接收事件对象
  • React中的事件对象同样不是原生的事件对象,是经过React包装后的事件对象
  • 由于对象进行过包装,所以使用过程中我们无需再去考虑兼容性问题

二、Props

基础用法

在组件中如果将数据全部写死,将会导致组件无法动态设置,不具有使用价值

我们希望组件数据可以由外部设置,在组件间,父组件可以通过props(属性)向子组件传递数据

父组件中可以直接在子组件标签中设置属性,作为传递的参数值

  • 在函数组件中,属性props就相当于是函数的参数,可以通过参数来访问
  • 可以在函数子组件的形参中定义一个props,props指向的是一个对象
  • 它包含了父组件中传递的所有参数

注意:props只读不能修改

//Logs.js
import LogItem from './LogItem/logItem.js'
const Logs = () => {
  // 模拟一组从服务器中拿回来的数据
  const logsData = [
    {
      id: '001',
      date: new Date(2021, 1, 20, 18, 30),
      desc: '学习九阳神功',
      time: 30
    },
    {
      id: '002',
      date: new Date(2022, 2, 10, 12, 30),
      desc: '学习降龙十八掌',
      time: 20
    }
  ];
  return (
    <div className='logs'>{
      logsData.map((item) => {
        // return <LogItem key={item.id} date={item.date} desc={item.desc} time={item.time} />
        return <LogItem {...item} />
      })
    }
    </div >
  )
}
export default Logs
//LogItem.js
import MyDate from './MyDate/MyDate.js'

import './LogItem.css'
const LogItem = (props) => {
  return (
    <div className='item'>
      <MyDate date={props.date}/>
      <div className='content'>
        <h2 className="desc">{props.desc}</h2>
        <div className="time">{props.time}分钟</div>
      </div>
    </div>
  )
}
export default LogItem
import './MyDate.css'
const MyDate = (props) => {
  const month = props.date.toLocaleString('zh-CN', { month: 'long' })
  const day = props.date.getDate()
  return (
    <div className='date'>
      <div className='month'>{month}</div>
      <div className='day'>{day}</div>
    </div>
  )
}
export default MyDate

传递标签体

怎么把标签体传给card:props.className

//Card.js
import './Card.css'
const Card=(props)=>{
  return (
    <div className={`${props.className} card`}>{props.children}</div>
  )
}
export default Card

三、State

在React中,当组件渲染完毕后(先执行),再修改组件中的变量(后执行),不会使组件重新渲染;要使得组件可以受到变量的影响,必须在变量修改后对组件进行重新渲染;这里我们就需要一个特殊变量,当这个变量被修改时,组件会自动重新渲染。

state相当于一个变量,只是这个变量在React中进行了注册,React会监控这个变量的变化,当state发生变化时,会自动触发组件的重新渲染,使得我们的修改可以在页面中呈现出来。

注意:props是传给别人用的,state是对当前组件自身的属性(state是可变的,只属于当前属性)

定义步骤:

  • 在函数组件中,我们需要通过钩子函数,获取state;
  • 使用钩子 useState() 来创建state;
  • 导入包:import {useState} from “react”;

    useState()需要一个值作为参数,这个值就是state的初始值

    该函数会返回一个数组

    数组中第一个元素,是初始值,初始值只用来显示数据,直接修改不会触发组件的重新渲染

    数组中的第二个元素,是一个函数,通常会命名为setXxx,这个函数用来修改state,调用其修改state后会触发组件的重新渲染(重新调用render),并且使用函数中的值作为新的state值

  • useState()需要一个值作为参数,这个值就是state的初始值

    该函数会返回一个数组

    数组中第一个元素,是初始值,初始值只用来显示数据,直接修改不会触发组件的重新渲染

    数组中的第二个元素,是一个函数,通常会命名为setXxx,这个函数用来修改state,调用其修改state后会触发组件的重新渲染(重新调用render),并且使用函数中的值作为新的state值

  • 该函数会返回一个数组
  • 数组中第一个元素,是初始值,初始值只用来显示数据,直接修改不会触发组件的重新渲染
  • 数组中的第二个元素,是一个函数,通常会命名为setXxx,这个函数用来修改state,调用其修改state后会触发组件的重新渲染(重新调用render),并且使用函数中的值作为新的state值
import { useState } from "react";
import './counter.css'
const Counter = () => {
  const [counter, setCounter] = useState(1);
  const addHandler = () => {
    setCounter(counter + 1)
  }
  const minusHandler = () => {
    setCounter(counter - 1)
  }
  return (
    <div className="counter">
      <h2>{counter}</h2>
      <button onClick={addHandler}>+</button>
      <button onClick={minusHandler}>-</button>
    </div>
  )
}
export default Counter

总结:

state实际就是一个被React管理的变量;当我们通过setState()修改变量的值时,会触发组件的自动重新渲染;只有state值发生变化时,组件才会重新渲染

当state的值是一个对象时,修改时是使用新的对象去替换已有对象

  • setUser({…user, name: ‘猪八戒’});
  • const newUser = Object.assign({}, user);
    newUser.name = ‘猪八戒’;
    setUser(newUser);

如果直接修改旧的state对象,由于对象还是那个对象,所以不会生效

  • user.name = ‘猪八戒’;
  • setUser(user);

当通过setState去修改一个state时,并不表示修改当前的state,counter的初始值并没有改变;它修改的是组件下一次渲染时state值

setState()会触发组件的重新渲染,它是异步的;调完setState组件并不是立刻调用render函数进行渲染,而是先挂载到队列中,直到代码所有都执行完成再执行队列

所以当调用setState()需要用旧state的值时,一定要注意,有可能出现计算错误的情况;为了避免这种情况,可以通过为setState()传递回调函数的形式来修改state值

    const addHandler = () => {
        setTimeout(() => {
            setCounter((prevCounter)=>{
                /*
                *   setState()中回调函数的返回值将会成为新的state值
                *       回调函数执行时,React会将最新的state值作为参数传递
                * */
                return prevCounter + 1;
            });
            // setCounter(prevState => prevState + 1);
        }, 1000);
    };

只要用到之前的值就传回调

四、Ref

useRef()返回的就是一个普通的JS对象 {current:undefined},所以我们直接创建一个js对象,也可以代替useRef()

const h1Ref = {current:null};

const h1Ref = useRef(); // 创建一个容器

区别:

  • 我们创建的对象,组件每次重新渲染都会创建一个新对象

  • useRef()创建的对象,可以确保每次渲染获取到的都是同一个对象

  • 当需要一个对象不会因为组件的重新渲染而改变时,就可以使用useRef()

获取原生的DOM对象步骤

  • 1.创建一个存储DOM对象的容器

    使用 useRef() 钩子函数

    ① React中的钩子函数只能用于函数组件或自定义钩子

    ② 钩子函数只能直接在函数组件中调用

  • 使用 useRef() 钩子函数

    ① React中的钩子函数只能用于函数组件或自定义钩子

    ② 钩子函数只能直接在函数组件中调用

  • ① React中的钩子函数只能用于函数组件或自定义钩子
  • ② 钩子函数只能直接在函数组件中调用
  • 2.将容器设置为想要获取DOM对象元素的ref属性

    React会自动将当前元素的DOM对象,设置为容器current属性

  • React会自动将当前元素的DOM对象,设置为容器current属性
  • 代码:
    < h1 id="header" ref={h1Ref}>我是标题{count}
    h1Ref.current.innerText = ‘嘻嘻!’;

五、受控/非受控组件

非受控组件:监听表单项的变化

  • 创建三个变量,用来存储表单中的数据
  • 创建一个响应函数,监听相关数据的变化

    函数的参数为e:当前触发事件的对象,事件对象中保存了当前事件触发时的所有信息

    event.target 执行的是触发事件的对象(DOM对象)

  • 函数的参数为e:当前触发事件的对象,事件对象中保存了当前事件触发时的所有信息
  • event.target 执行的是触发事件的对象(DOM对象)
  • 在React中,通常表单不需要自行提交,而是要通过React提交

    取消表单的默认行为

    获取表单项中的数据

    将数据拼装为一个对象

  • 取消表单的默认行为
  • 获取表单项中的数据
  • 将数据拼装为一个对象
//LogsForm.js
import Card from "../UI/Card"
import './LogsForm.css'
const LogsForm = () => {
  let inputDate = ''
  let inputDesc = ''
  let inputTime = 0
  const dateChangeHandler = (e) => {
    inputDate = e.target.value
  }
  const descChangeHandler = (e) => {
    inputDesc = e.target.value
  }
  const timeChangeHandler = (e) => {
    inputTime = e.target.value
  }
  const formSubmitHandler=(e)=>{
    e.preventDefault()
    const newLog={
      date:new Date(),
      desc: inputDesc,
      time: +inputTime
    }
  }
  return (
    <Card className="logs-form">
      <form onSubmit={formSubmitHandler}>
        <div className="form-item">
          <label htmlFor="date">日期</label>
          <input id="date" type="date" onChange={dateChangeHandler} />
        </div>
        <div className="form-item">
          <label htmlFor="desc">内容</label>
          <input id="desc" type="text" onChange={descChangeHandler} />
        </div>
        <div className="form-item">
          <label htmlFor="time">时长</label>
          <input id="time" type="number" onChange={timeChangeHandler} />
        </div>
        <div className="form-btn">
          <button >添加</button>
        </div>
      </form>
    </Card>
  )
}
export default LogsForm

受控组件(双向绑定)

将表单中的数据存储到state中,然后将state设置为表单项value值,当表单项发生变化,state会随之变化,反之,state发生变化,表单项也会跟着改变

state提升,提升到共用组件里

//app.js
import LogsForm from './Components/LogsForm/LogsForm'
import Logs from './Components/Logs/Logs'
import Counter from './Components/Counter/Counter.js'
import { useState } from 'react';
import './app.css'
const App = () => {
  // 模拟一组从服务器中拿回来的数据
  const [logsData, setLogsData] = useState([
    {
      id: '001',
      date: new Date(2021, 1, 20, 18, 30),
      desc: '学习九阳神功',
      time: 30
    },
    {
      id: '002',
      date: new Date(2022, 2, 10, 12, 30),
      desc: '学习降龙十八掌',
      time: 20
    }
  ])
  const saveLogHandler = (newLog) => {
    let id = Date.now() + ''
    newLog = { id, ...newLog }
    setLogsData([...logsData, newLog])
  }
  return (
    <div className='app'>
      <LogsForm onSaveLog={saveLogHandler} />
      <Logs logsData={logsData} />
      <Counter />
    </div>
  )
}
export default App
//Logs.js
props.onSaveLog(newLog)