1. 基本语法

1/9/2023 react

react应用程序是由组件组成的,一个组件是UI(用户页面)的一部分,它拥有自己的逻辑和外观。组件可以小到一个按钮,也可以大到整个页面。

React 组件必须以大写字母开头,而 HTML 标签则必须是小写字母。

# 一、两种写法

以前,React API 只有一套,现在有两套:类(class)API 和基于函数的钩子(hooks) API

# 1. 类的写法

class Welcome extends React.Component {
	// 初始化类组件的 state
  state = {
    text: ""
  };

	componentDidMount() {
    // 省略业务逻辑
  }

	changeText = (newText) => {
    // 更新 state
    this.setState({
      text: newText
    });
  };

	// 编写生命周期方法 render
  render() {
    return 
			<div>
			  <h1>Hello, {this.props.name}</h1>;
	      <p>{this.state.text}</p>
	      <button onClick={()=>this.changeText("newText")}>点我修改</button>
	    </div>
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 特点

  • 为了避免代码冗余,提高代码利用率,组件可以重复调用
  • 组件的属性props是只读的,调用者可以传递参数到props对象中定义属性,调用者可以直接将属性作为组件内的属性或方法直接调用。往往是组件调用方调用组件时指定props定义属性,往往定值后就不改边了,注意组件调用方可赋值被调用方。
  • 通过props的方式进行父子组件交互,通过传递一个新的props属性值使得子组件重新render,从而达到父子组件通讯。
  • {...this.props}可以传递属性集合,...为属性扩展符
  • 组件必须返回了一个 React 元素
  • 组件中state为私有属性,是可变的,一般在construct()中定义,使用方法:不要直接修改 state(状态)
  • 修改子组件还有一种方式,通过 ref属性,表示为对组件真正实例引用,其实就是ReactDOM.render()返回的组件实例

# 2. 钩子的写法(函数)【推荐】

何谓函数组件/无状态组件(Function Component/Stateless Component)
函数组件顾名思义,就是以函数的形态存在的 React 组件。早期并没有 React-Hooks 的加持,函数组件内部无法定义和维护 state,因此它还有一个别名叫“无状态组件”。
React.FC 是 TypeScript 中用于定义函数组件的泛型接口。它在 React 16.9 版本引入,因此从 React 16.9 开始,React.FC 写法就可以生效。

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

const QuestionCard: React.FC = () => {
  return (
    <>
      <h1>问卷列表1</h1>
    </>
  )
}
export default QuestionCard
1
2
3
4
5
6
7
8
9
10
11
12

# 特点

  • 只负责接收 props,渲染 DOM
  • 没有 state
  • 返回了一个 React 元素
  • 不能访问生命周期方法
  • 不需要声明类:可以避免 extends 或 constructor 之类的代码,语法上更加简洁。
  • 不会被实例化:因此不能直接传 ref(可以使用 React.forwardRef 包装后再传 ref)。
  • 不需要显示声明 this 关键字:在 ES6 的类声明中往往需要将函数的 this 关键字绑定到当前作用域,而因为函数式声明的特性,我们不需要再强制绑定。
  • 更好的性能表现:因为函数式组件中并不需要进行生命周期的管理与状态管理,因此React并不需要进行某些特定的检查或者内存分配,从而保证了更好地性能表现。

无状态组件(展示组件,函数式组件)
stateless组件写法:所谓stateless组件,也就是无状态组件。这种react组件有一个特点,它没有生命周期方法,没有render方法,连state也没有,this也没有,也不需要实例化。
为什么需要这样的组件?
很多时候,从业务上考虑,我们的某些组件只用于纯UI展示,并没有涉及到生命周期,也不需要setState, 但是react组件本身依然存在生命周期方法等一大堆组件本身的设定,然而这些设定我们根本不需要用到的,它们的存在造成了资源浪费,多余,和臃肿。 另外组件实例化是需要占用内存,消耗性能的。
因此,考虑到不同业务需求,后来react增加了stateless组件的支持。
stateless组件本质是一个函数,它没有生命周期、没有this指向、也不需要实例化,更轻盈,性能更加好。
这种组件是所有react组件中性能最好的组件类型。官方也推荐多用这种组件。
stateless组件本质是一个带有返回值的函数,而且必须是使用闭合标签包裹的返回值。

const AppComponent = (props) =>{
  // 一些逻辑
  return <div>
   这是一个干净纯洁的stateless组件
  </div>
}
1
2
3
4
5
6

# 共同点:

接收值:都一样接收了一个只读的 props 返回值:都是返回了一个 React 元素

# 不同点:

# 设计思想层面

  1. 类组件:面向对象编程(OOP),所以它有属性、继承、内部的状态管理
  2. 函数组件:函数式编程(FP),属于”结构化编程“的一种,与数学思想类似。也就是假定输入与输出存在某种特定的映射关系,那么输入一定的情况下,输出必然是确定的。

# 能力

  • 类组件通过生命周期包装业务逻辑
  • 函数组件可以通过React Hooks 钩子函数来模拟类组件中的生命周期(useState ,useEffect,useContext,useCallback 等)

Hooks 的本质:一套能够使函数组件更强大、更灵活的“钩子”

# 使用场景

  • 在不使用 Recompose 或者 Hooks 的情况下,如果需要使用生命周期,那么就用类组件,限定的场景是非常固定的;
  • 但在 recompose 或 Hooks 的加持下,这样的边界就模糊化了,类组件与函数组件的能力边界是完全相同的,都可以使用类似生命周期等能力。

# 发展趋势

函数组件更加契合React框架的设计理念
React组件本身的定位就是函数,一个吃进函数,吐出UI的函数。作为开发者,我们编写的是声明式的代码,而 React 框架的主要工作,就是及时地把声明式的代码转换为命令式的 DOM 操作,把数据层面的描述映射到用户可见的 UI 变化中去。这就意味着从原则上来讲,React 的数据应该总是紧紧地和渲染绑定在一起的,而类组件做不到这一点。 由于 React Hooks 的推出,函数组件成了社区未来主推的方案。
React 团队从 Facebook 的实际业务出发,通过探索时间切片与并发模式,以及考虑性能的进一步优化与组件间更合理的代码拆分结构后,认为类组件的模式并不能很好地适应未来的趋势。 他们给出了3 个原因:

this 的模糊性;

业务逻辑散落在生命周期中;

React 的组件代码缺乏标准的拆分方式。

而使用 Hooks 的函数组件可以提供比原先更细粒度的逻辑组织与复用,且能更好地适用于时间切片与并发模式。

# React Hooks

use开头的函数被称为Hook。React 16.8.0 是第一个支持 Hooks 的版本。

# 二、useState的使用

function Screen(){
	//  变量:curTitle,方法:setCurTitle去更新变量的值
	const [ curTitle, setCurTitle ] = useState('')
	
	useEffect(() => {
		
		const mockRequst = () => {
      const data = {title: ''}
      return new Promise((resolve) => {
        setTimeout(() => {
          data.title = 'xxx'
          resolve(data)
        }, 1000)
      })

    }

    const loadData = async () => {
      const res: any = await mockRequst()
      console.log('res', res)
      setCurTitle(res?.title)
      console.log('curTitle', curTitle)
    }
    loadData()

    console.log('最后的')
		
	})

	return (
		<div>
        <span> { curTitle } </span>
        <span> 最后的 </span>
     </div>
	)
}

export {
	Screen
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

# 三、useEffect的使用 (更新DOM)

useEffect(() => {},[])
1

# useEffect执行两次

在严格模式下,React为什么要将useEffect执行两次
严格模式只会在开发环境下运行,在生产环境下它是没有影响的

ReactDOM.createRoot(document.getElementById('root')!).render(
  // StrictMode:严格模式下,开发环境会渲染两次(组件重新渲染一次,查找Effect有没有副作用),打包之后的生产环境只会渲染一次。
  <React.StrictMode>
    <ConfigProvider locale={zhCN}>
      <App />
    </ConfigProvider>
  </React.StrictMode>
)
1
2
3
4
5
6
7
8

# 四、useCallback

  • 它是用来缓存函数的,useMemo 返回一个 memoized 回调函数。
  • useCallback用来返回一个函数,在父子组件传参或者通用函数封装中,起到举足轻重的作用。
  • useCallback的用法与useState的用法基本一致,但最后会返回一个函数,用一个变量保存起来。
const a = useCallback(() => {
	return function() {
		console.log(b)
	}
},[b])

console.log(a)
console.log(a())

useCallback(fn, deps) 相当于 useMemo(() => fn, deps)
1
2
3
4
5
6
7
8
9
10

# 五、useMemo 返回一个 memoized 值(记忆函数)

把“创建”函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算。

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
1

注意

  1. 记住,传入 useMemo 的函数会在渲染期间执行。请不要在这个函数内部执行不应该在渲染期间内执行的操作,诸如副作用这类的操作属于 useEffect 的适用范畴,而不是 useMemo。
  2. 如果没有提供依赖项数组,useMemo 在每次渲染时都会计算新的值。
  3. 你可以把 useMemo 作为性能优化的手段,但不要把它当成语义上的保证。

# 六、事件命名采用小驼峰方法

// 小驼峰:activeBtn
<button onClick={activeBtn}>导出</button>
1
2
最近更新时间: 10/11/2023, 7:44:50 AM
강남역 4번 출구
Plastic / Fallin` Dild