React学习笔记

本文介绍了React的脚手架创建项目,以及ReactHooks的常用功能,如useState进行状态管理,useEffect处理副作用,useContext实现跨组件通信,useReducer在复杂状态管理中的应用,useMemo和useCallback优化性能,以及useRef获取DOM节点和存储数据,最后讲解了forwardRef用于组件间传递ref。
摘要由CSDN通过智能技术生成

1. react脚手架

初始化项目

npx create-react-app my-app

2. Hooks

以 use 开头的函数,只能在组件或自定义 Hook 的最顶层调用,不能在条件语句、循环语句或其他嵌套函数内调用 Hook。

useState

在组件中添加状态变量

const [state, setState] = useState(initialState);

useEffect

侦听状态变化,并执行相应逻辑

useEffect(setup, dependencies?)

不带参数的 useEffect 函数中不能使用useState方法来改变state,否则会触发无限循环

//不带参数,每次渲染后执行
useEffect(() => {
    console.log("count", count);
});

当 useEffect 带参数时,参数的变化才会触发 useEffect 的执行,不变化不执行。可使用 useState

useEffect(() => {
    console.log("count", count);
    console.log("num", num);
}, [num]);

当 useEffect 传入空参数 [] 时,只有当第一次渲染(mount) 和不渲染(unmount)时执行。

useEffect(() => {
    console.log("count", count);
}, []);

useContext

父子组件传递状态数据。createContext 能够创建组件间共享的上下文状态,然后通过 useContext 在组件中使用这些状态

import { createContext, useContext } from 'react';

const ThemeContext = createContext(null);

export default function MyApp() {
  return (
    <ThemeContext.Provider value="dark">
      <Button>显示的主题</Button>
    </ThemeContext.Provider>
  )
}

function Button({ children }) {
  const theme = useContext(ThemeContext);
  const className = 'button-' + theme;
  return (
    <button className={className}>
      {children}
    </button>
  );
}

useReducer

useReducer 是 useState 的替代方案。当你想更新的状态中包含多个子值时 或者 当你想更新一个状态,并且这个状态更新依赖于另一个状态的值时,可以使用useReducer,reducer 可以让你把组件内发生了什么(actions)和状态如何响应并更新分开表述。
结合useContext 可以实现 Redux 组件间共享状态管理

const [state, dispatch] = useReducer(reducer, initialArg, init?)

第三个参数如果没值,则将初始状态设置为initialArg。否则,将初始状态设置为呼叫init(initialArg)的结果

import { useReducer } from 'react';

function reducer(state, action) {
  switch (action.type) {
    case "minus_age":
      return {
        age: state.age > 0 ? (state.age - 1) : 0
      };
    default:
      return {
        age: state.age + 1
      };
  }
}

export default function Counter() {
  const [state, dispatch] = useReducer(reducer, { age: 42 });
  return (
    <>
      <button onClick={() => {
        dispatch({ type: 'add_age' })
      }}>
        Add age
      </button>
      <button onClick={() => {
        dispatch({ type: 'minus_age' })
      }}>
        Minus age
      </button>
      <p>Hello! You are {state.age}.</p>
    </>
  );
}

和useState的setState不一样的是,useReducer返回的dispatch函数是用来触发某些改变state的action而不是直接设置state的值

useMemo

相当于计算属性

const cachedValue = useMemo(calculateValue, dependencies)
 export default function Counter() {
   const clickFn = useMemo(() => {
      let sum = 0;
      for (let i = 0; i < 100*count; i++) {
        sum += i;
      }
      return sum;
   },[count]);
   //当依赖的count发生改变才去重新计算,return值出来
   
   return(
     <div>获得总数{clickFn}</div>
   )   
}

useCallback

避免子组件不必要的重新渲染,依赖数据改变传入新函数否则保留原函数

const cachedFn = useCallback(fn, dependencies)

useMemo和useCallback接收的参数都是一样,都是在其依赖项发生变化后才执行,都是返回缓存的值。而useEffect没有返回
区别在于useMemo返回的是函数运行的结果,useCallback返回的是函数。
useCallback(fn, deps) 与 useMemo(() => fn, deps). 是等价的,

useRef

useRef 返回一个引用了DOM的对象,返回的对象将在组件的整个生存期内持续存在。相当于为this赋值

const ref = useRef(initialValue)
import { useRef } from 'react';

export default function Form() {
  const inputRef = useRef(null);
  
  function handleClick() {
    //点击使input框获取焦点
    inputRef.current.focus();
  }

  return (
    <>
      <input ref={inputRef} />
      <button onClick={handleClick}>
        Focus the input
      </button>
    </>
  );
}

useRef和createRef的区别是createRef 每次渲染都会返回一个新的引用,而 useRef 每次都会返回相同的引用,可以在 useRef 对象上存放缓存的值。

3. API

forwardRef

转发ref,将dom暴露给父组件

import { forwardRef, useRef } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
  return (
    <label>
      {props.label}
      <input ref={ref} />
    </label>
  );
});

export default function Form() {
  const ref = useRef(null);

  function handleClick() {
    ref.current.focus();
  }

  return (
    <form>
      <MyInput label="Enter your name:" ref={ref} />
      <button type="button" onClick={handleClick}>
        Edit
      </button>
    </form>
  );
}

使用forwardRef和useImperativeHandle限制父组件调用子组件的Api

import { forwardRef, useRef, useImperativeHandle } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
  const inputRef = useRef(null);
  
  useImperativeHandle(ref, () => {
    return {
      focus() {
        inputRef.current.focus();
      },
      scrollIntoView() {
        inputRef.current.scrollIntoView();
      },
    };
  }, []);
  
  return <input {...props} ref={inputRef} />
});

export default function Form() {
  const ref = useRef(null);

  function handleClick() {
    //父组件获得了 MyInput 的 ref,能通过该 ref 来调用 focus 和 scrollIntoView 方法
    //但是它的访问是受限的,无法读取或调用下方 <input> DOM 节点的其他所有属性和方法
    ref.current.focus();
    // 下方代码不起作用,因为 DOM 节点并未被暴露出来:
    // ref.current.style.opacity = 0.5;
  }

  return (
    <form>
      <MyInput label="Enter your name:" ref={ref} />
      <button type="button" onClick={handleClick}>
        Edit
      </button>
    </form>
  );
}

ref有一个current属性,可以对这个属性进行操作,用于获取DOM元素和保存变化的值。
不要滥用ref,仅在没法通过 prop 来表达 命令式 行为的时候才使用 ref,比如滚动到指定节点、聚焦某个节点、触发一次动画、选择文本等
如果可以通过prop实现,就不要使用ref ,useEffect 可以通过 prop 来暴露一些命令式的行为

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值