同一组件内部,通过useref来获取某一个dom标签以及dom标签的方法。

在以前使用ref的时候,我们经常用ref来保存某个需要的dom标签,这样需要用到的时候,就可以不用去寻找这个dom标签了,在js中,通过方法,parentNode,lastchild,nextElementsibling 这种关系结构来找dom是一件比较麻烦的事情。如果不是动态类名或者id的话,通过document.getElementsByClassName 之类的方法,虽然可行,却也比较麻烦,因为得到的是一个dom数组。

看看useref如何实现

  1. import React, { useState, useEffect, useMemo, useRef } from 'react';
  2. export default function App(props){
  3. const [count, setCount] = useState(0);
  4. const doubleCount = useMemo(() => {
  5. return 2 * count;
  6. }, [count]);
  7. const couterRef = useRef(); //创建useRef
  8. useEffect(() => {
  9. document.title = `The value is ${count}`;
  10. //通过couterRef 的current 属性可以获取到button这个dom
  11. console.log(couterRef.current);
  12. }, [count]);
  13. return (
  14. <>
  15. //与ref类似,绑定到button标签上
  16. <button ref={couterRef} onClick={() => {setCount(count + 1)}}>Count: {count}, double: {doubleCount}</button>
  17. </>
  18. );
  19. }

跨组件获取子组件的方法。

在以往的ref中,可以将ref绑定到某个子组件标签上,用以获取整个子组件的方法和参数,在react 的useref也有类似的功能。

父组件代码

  1. import React, { useRef } from 'react';
  2. import StudentStatusForm from './components/student-status-info-emdit//student-status-info-emdit';
  3. const StudentBase: React.FC = (props) => {
  4. const onClickConfirmStudentStatusInfo = () => {
  5. //获取StudentStatusForm 子组件onFinish方法
  6. userInfoStatus.current
  7. .onFinish()
  8. .validateFields()
  9. .then(() => {
  10. setStudentInfoStatus(0);
  11. });
  12. };
  13. const userInfoStatus = useRef();
  14. return (
  15. <>
  16. //当点击Button的时候获取StudentStatusForm 子组件的onFinish方法
  17. <Button onClick={onClickConfirmStudentStatusInfo }>获取onFinish方法</Button>
  18. <StudentStatusForm ref={userInfoStatus} initialValues={studentDetailsData} />
  19. </>
  20. )}
  21. export default StudentBase;

子组件代码

  1. import React, { useState, useEffect, forwardRef, useImperativeHandle } from 'react';
  2. import {Form} from 'antd';
  3. //子组件不再使用react的React方式来创建,直接通过函数的方式创建,函数子组件接收两个参数,第一个参数是父组件传过来的其它参数,第二个参数是ref
  4. const StudentStatusForm = (props: StudentStatusFormType, ref: any) => {
  5. const { initialValues } = props;
  6. const [form] = Form.useForm();
  7. const onFinish = () => {
  8. return form;
  9. };
  10. useEffect(() => {
  11. const newInitialValues = {...initialValues}
  12. newInitialValues.admissionDate = moment(initialValues.admissionDate);
  13. newInitialValues.birthday = moment(initialValues.birthday);
  14. form.setFieldsValue(newInitialValues);
  15. }, [initialValues]);
  16. //react规定,必须使用useImperativeHandle方法,来保存并抛出想要传递给父组件的方法或者数据,第一个参数是ref,第二个参数是函数,返回想要抛出的对象集合
  17. useImperativeHandle(ref, () => ({
  18. onFinish,
  19. }));
  20. return (
  21. <div className="student-status-info-emdit">
  22. <Form {...formItemLayout} form={form}>
  23. </Form>
  24. </div>
  25. );
  26. };
  27. //必须通过forwardRef方法抛出函数组件
  28. export default forwardRef(StudentStatusForm);

用来保存数据并且不重新渲染页面

不常用却好用的useref骚操作
在react中,无论是使用usestate来保存数据,如果这数据被修改,将会引起页面的重新渲染。

可有时候,我们想要保存的数据不大,但当数据改变的时候不想页面重新渲染,这份数据在页面多次重新渲染后仍然能保持不变。

要做到以上条件使用什么方法呢?
1 reducer 感觉多少还是有些麻烦,
2 保存到浏览器locationstoreage,取的时候也麻烦,不安全。
3 保存到服务器,可能你会被骂死,这么一点小数据还要麻烦后端。
4 保存到useref里面去。

  1. import React, { useState, useEffect, useMemo, useRef } from 'react';
  2. export default function App(props){
  3. const [count, setCount] = useState(0);
  4. const doubleCount = useMemo(() => {
  5. return 2 * count;
  6. }, [count]);
  7. // 创建useRef
  8. const timerID = useRef();
  9. useEffect(() => {
  10. // 不从useRef取值,而是赋值给它,用来保存定时器格外酸爽
  11. timerID.current = setInterval(()=>{
  12. setCount(count => count + 1);
  13. }, 1000);
  14. }, []);
  15. useEffect(()=>{
  16. if(count > 10){
  17. // 当你需要使用的时候,依然存在,停掉定时器,
  18. clearInterval(timerID.current);
  19. }
  20. });
  21. return (
  22. <>
  23. //绑定ref
  24. <button ref={couterRef} onClick={() => {setCount(count + 1)}}>Count: {count}, double: {doubleCount}</button>
  25. </>
  26. );
  27. }

当useRef与typescript相遇限制useRef的类型

子组件

  1. import React, { useEffect, useState, forwardRef, useImperativeHandle, memo } from 'react';
  2. import { Modal, Form } from 'antd';
  3. type ModalDetailsProps = {
  4. };
  5. export interface ModalDetailsRefInterface {
  6. initDataFunction: (id:number) => void;
  7. }
  8. const DetailsModal = forwardRef<ModalDetailsRefInterface, ModalDetailsProps>((_, ref) => {
  9. const [formRef] = Form.useForm();
  10. /** 控制modal是否显示 */
  11. const [modalVisible, setModalVisible] = useState<boolean>(false);
  12. const onCancel = () => {
  13. setModalVisible(false);
  14. };
  15. /** 获取id,请求详情数据 */
  16. const initDataFunction = (id:number) => {
  17. console.log(id)
  18. setModalVisible(true);
  19. }
  20. /** 将初始化数据方法抛出给父组件 */
  21. useImperativeHandle(ref, () => ({
  22. initDataFunction
  23. }));
  24. const modalProps = {
  25. visible: modalVisible,
  26. title: '查看详情',
  27. width: 500,
  28. onCancel,
  29. footer: false,
  30. };
  31. useEffect(() => {
  32. if (modalVisible) {
  33. formRef.resetFields();
  34. }
  35. }, [modalVisible, formRef]);
  36. return (
  37. <Modal getContainer={false} {...modalProps}>
  38. <div>123</div>
  39. </Modal>
  40. );
  41. });
  42. export default memo(DetailsModal);

父组件

  1. import React, { useRef } from 'react';
  2. import { Button } from 'antd';
  3. import DetailsModal, {
  4. ModalDetailsRefInterface,
  5. } from '../funding-application-details/funding-application-details';
  6. const FundingApplicationList: React.FC = () => {
  7. /** 申请资助学生详情 */
  8. const DetailsModalRef = useRef<ModalDetailsRefInterface>(null);
  9. /** 点击查看详情 */
  10. const onClickViewDetails = (fundingId: number) => {
  11. DetailsModalRef.current?.initDataFunction(fundingId);
  12. };
  13. return <>
  14. <Button
  15. onClick={() => {
  16. onClickViewDetails(2);
  17. }}
  18. >
  19. 查看详情
  20. </Button>
  21. <DetailsModal ref={DetailsModalRef} />
  22. </>
  23. }