React 初级教学

400 阅读5分钟

React 初级教学

背景历史

React 是 Facebook 在 2013 年开源的一款前端框架, Facebook 工程师开始打造自己的前端框架,解决前端一系列的问题,于是 React 就诞生啦~,详情请访问zhuanlan.zhihu.com/p/159472034

基础

分为class组件和function组件,也就是有状态组件和无状态组件。 今天主要讲function组件中的hook的使用。

  1. useState
  2. useCallback、useMemo/memo
  3. useEffect

1.useState

这个hook是最常使用的,当然也非常的容易理解。就是在函数组件中使用 state ,state改变后函数组件会重新渲染,和class组件一样。相当于vue3重的ref、reactive

使用事例
const [count, setCount] = useState<number>(0);
代码块事例
const [count, setCount] = useState<number>(0);
	//第一种方式 直接传入参数改变count
	const onClick = () => {
		setCount(count + 1);
	};

	//第二种方式 传入函数,函数返回值将为新的count
	const onClickTwo = () => {
		//prevState就是还没改变之前的值,也就是当前的值
		const actionFn = (prevState: number) => prevState + 1;
		setCount(actionFn);
	};

	//组件渲染时候在这里log记录
	console.log('App组件渲染');

	return (
		<div className="App">
			<header className="App-header">
				<img src={logo} className="App-logo" alt="logo" />
				<p>
					<Button onClick={onClick}>第一种方式 加1</Button>
					<Button onClick={onClickTwo}>第二种方式 加1</Button>
				</p>
				<ShowCount count={count} />
			</header>
		</div>
	);

掘金录屏.gif 只要调用 setCount方法页面(包括页面中的button组件)就会重新渲染一遍,state也会更新.因为Button组件引用了两遍,所有Button组件在state改变后总是会渲染两次。

这里只是想让页面显示的 count:[n] 后面的数字重新渲染,也就是说只想重新渲染Index页面,可不想重新渲染按钮,而且更不想重新渲染两遍按钮😰。

🤔 怎么优化?

2.useCallback、useMemo、memo

✨ so easy, 使用 useCallback + useMemo / React.memo 即可。

useCallback 说明 : useCallback 接受两个参数,第一参数是个函数,第二个参数是个数组,数组的某个元素如果被改变了的话第一个参数也就是传入的函数就会被更新,否则就不更新传入的函数,如果为空数组[],fn只执行一次。

使用事例
const fooFn = ()=>{} 
const callBackFooFn = useCallback(fooFn, [])

useMemo 说明 :
useMemo 调用方式和 useCallback 差不多一样。useMemo(fn, arr)第二个参数匹配,并且其值发生改变,才会多次执行执行,否则只执行一次,如果为空数组[],fn只执行一次。fn可以用于返回复杂计算的值,也可以返回一个组件。

使用事例
const fooFn = ()=>{} 
const callBackFooFn = useMemo(fooFn, [])

memo 说明 :
如果你的组件在相同 props 的情况下渲染相同的结果,那么你可以通过将其包装在 React.memo 中调用,以此通过记忆组件渲染结果的方式来提高组件的性能表现。这意味着在这种情况下,React 将跳过渲染组件的操作并直接复用最近一次渲染的结果。

如果组件的props没有改变的话,那可以使用 memo 函数让子组件不进行重复的渲染。
memo是一个高阶函数,同样接收两个参数,第一个参数为组件,第二个参数为一个函数,函数返回true的话组件将不更新,返回false组件将会更新。

优化后的代码块事例

方案一:useCallback + useMemo的使用。

//App组件
import React, { useState, useCallback } from 'react';
import Button  from './Button';

function App (){ 

   const [count, setCount] = useState(0); 
     
  //使用useCallback 方法包裹 第二个参数我们给空数组 让这个函数永远不会更新
  //注意这个函数永远不会更新的话 setCount 就必须写成funtion 不然拿不到想要的count状态
  const onClick = useCallback(()=> { 
    setCount(prevState => prevState + 1)
  }, [])  
  
  //组件渲染时候在这里log记录
	console.log('App组件渲染');
  
  return (
   	<div className="App">
	  <header className="App-header">
	   <img src={logo} className="App-logo" alt="logo" />
           {/* 使Button组件也永远不要更新 */}
           { useMemo(()=><Button onClick={onClick}>第二种方式 加1</Button> , []}) }
           <ShowCount count={count} />
          </header>
	</div>
	);
}
export default App;
 

✨执行效果如下

掘金录屏.gif

方案二:useCallback + memo的使用。

//App组件
function App() {
	const [count, setCount] = useState<number>(0);

	//使用useCallback 方法包裹 第二个参数我们给空数组 让这个函数永远不会更新 
        //注意这个函数永远不会更新的话 setCount 就必须写成funtion 不然拿不到想要的count状态(必须以第二种方法去写,第一种方法拿不到count状态)
	const onClickTwo = useCallback(() => {
		//prevState就是还没改变之前的值,也就是当前的值
		const actionFn = (prevState: number) => prevState + 1;
		setCount(actionFn);
	}, []);

	//组件渲染时候在这里log记录
	console.log('App组件渲染');

	return (
		<div className="App">
			<header className="App-header">
				<img src={logo} className="App-logo" alt="logo" />
				<p>
					<Button onClick={onClickTwo}>第二种方式 加1</Button>
				</p>
				<ShowCount count={count} />
			</header>
		</div>
	);
}

Button组件

import React, { memo } from 'react';
interface ButtonProps {
	children: any;
	onClick: any;
}
const Button = ({ onClick, children }: ButtonProps) => {
	//组件渲染时候在这里log记录
	console.log('Botton组件渲染');

	return <button onClick={onClick}>{children}</button>;
};
//用memo包裹
export default memo(Button);

✨执行效果如下

掘金录屏.gif

方案三:memo的使用。

memo 的另一个奇淫技巧。(一般不推荐使用,熟练后根据情况使用)

App组件

function App() {
	const [count, setCount] = useState<number>(0);
	
	//第二种方式 传入函数,函数返回值将为新的count
	const onClickTwo = () => {
		//prevState就是还没改变之前的值,也就是当前的值
		const actionFn = (prevState: number) => prevState + 1;
		setCount(actionFn);
	};

	//组件渲染时候在这里log记录
	console.log('App组件渲染');

	return (
		<div className="App">
			<header className="App-header">
				<img src={logo} className="App-logo" alt="logo" />
				<p>
					
					<Button onClick={onClickTwo}>第二种方式 加1</Button>
				</p>
				<ShowCount count={count} />
			</header>
		</div>
	);
}

Button组件

import React, { memo } from 'react';
interface ButtonProps {
	children: any;
	onClick: any;
}
const Button = ({ onClick, children }: ButtonProps) => {
	//组件渲染时候在这里log记录
	console.log('Botton组件渲染');

	return <button onClick={onClick}>{children}</button>;
};
export default memo(Button, (prevProps, nextProps)=>{	
	//可以使用prevProps, nextProps进行一些逻辑判断
	return true
})

✨执行效果如下

掘金录屏.gif 🤔原来单独使用 memo 也可以搞定 useCallback + useMemo组合。
但是需要注意:当 memo 第二个参数返回 true 时候组件将不会被更新,也就是上一次的 props 的更新将不会用到组件中,可能会造成一些奇怪的问题。所以如果你控制不住的话,最好不要使用🤣。

3.useEffect

集合了组件中的 componentDidMount,componentDidUpdate以及componentWillUnmount到一个方法中,杜绝了频繁定义的繁琐。相当于vue中的mounted、beforeUpdate、beforeDestroy。

使用事例
useEffect(()=>{},[depts])

1.如果传递一个空数组 [ ],告诉 useEffect 不依赖于 state、props中的任意值, useEffect 就只会运行一次,相当于componentDidMount,vue中的mounted
2.如果数组中有值,这个值改变就会触发运行
3. 如果不给数组,useEffect(()=>{}),相当于componentDidUpdate,也就是vue中的beforeUpdate
4. 方法里写return ...,相当于componentWillUnmount,也就是vue中的beforeDestroy\

其他hooks

useRef、useContext、useReducer、useLayoutEffect...

事例代码gitee.com/wangsf1/lea…