如果你有Vue 基础,可以对比学习。如果你只是React 新手,那这篇“组件大集合” 很适合你
从组件通信到表单管理,再到异步渲染,React提供了强大的工具和灵活的机制,帮助开发者高效地构建交互式应用。无论是父子组件之间的数据传递,还是通过createPortal实现的全局组件渲染,亦或是Suspense带来的优雅异步加载体验,React都展现出了其独特的优势。
下面多采用TS 方式,可以当成TS规范了属性样式去学习。
一、组件通信:父子组件的“对话”
1. 父组件向子组件传递props
父组件就像一个“家长”,把一些信息(props)传递给子组件。子组件通过函数的参数接收这些props
props是一个对象,里面可以包含各种数据,比如字符串、数字、数组、函数等。举个例子:
// 父组件
const ParentComponent = () => {
const name = "小微";
return <ChildComponent name={name} />;
};
// 子组件
const ChildComponent: React.FC<{ name: string }> = ({ name }) => {
return <div>Hello, {name}!</div>;
};
在上面的例子中,ParentComponent把name这个props传递给ChildComponent,ChildComponent通过解构的方式接收name,并在页面上显示出来。
(是不是很像Vue 父传子)
2. 子组件不能修改props
React规定了单向数据流,子组件不能直接修改父组件传递过来的props。这是因为如果子组件可以随意修改props,就会导致数据流向混乱,难以追踪数据的变化。
React的源码中会使用Object.freeze来冻结props,防止子组件修改它。Object.freeze是一个非常强大的方法,它可以让一个对象“冻结”,不能添加新属性、删除现有属性,也不能修改属性的值、可枚举性、可配置性等。
这样保证性能更好,副作用更少——确定子组件不能修改props 信息,也减少不必要的渲染。
3. 父组件通过回调函数接收子组件的“消息”
虽然子组件不能直接修改props,但它可以通过回调函数把一些信息传递回父组件。父组件把一个函数作为props传递给子组件,子组件在需要的时候调用这个函数,就像是给父组件发了一个“消息”。比如:
// 父组件
const ParentComponent = () => {
const handleChildMessage = (message: string) => {
console.log("Child says:", message);
};
return <ChildComponent onMessage={handleChildMessage} />;
};
// 子组件
const ChildComponent: React.FC<{ onMessage: (message: string) => void }> = ({ onMessage }) => {
return <button onClick={() => onMessage("Hello from child!")}>Send Message</button>;
};
在这个例子中,ChildComponent有一个按钮,点击按钮时会调用onMessage函数,并传递一个消息给ParentComponent。(Vue 里面就是使用emit事件完成一样功能)
二、受控组件与非受控组件:表单元素的管理方式
1. 受控组件
受控组件是React管理表单元素的一种方式。它适用于大部分表单元素,如input、textarea、select等(除了input type="file")。
在受控组件中,表单元素的值是由React的state来控制的。这意味着,表单元素的值会随着state的变化而变化,而用户对表单元素的输入也会触发state的更新。例如:
import React, { useState } from 'react';
const ControlledForm = () => {
const [inputValue, setInputValue] = useState('');
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setInputValue(e.target.value);
};
return (
<input type="text" value={inputValue} onChange={handleChange} />
);
};
在这个例子中,input的值是由inputValue这个state来控制的,用户输入的内容会通过handleChange函数更新inputValue,从而实现受控。
2. 非受控组件
非受控组件则是表单元素的值由DOM自己管理,而不是React的state。我们可以通过 useRef 来获取表单元素的值。这种方式在处理input type="file"时比较常用,因为文件输入的值很难通过state来控制。例如:
import React, { useRef } from 'react';
const UncontrolledForm = () => {
const inputRef = useRef<HTMLInputElement>(null);
const handleSubmit = () => {
console.log(inputRef.current?.value);
};
return (
<>
<input type="text" ref={inputRef} />
<button onClick={handleSubmit}>Submit</button>
</>
);
};
在这个例子中,我们通过useRef获取了input的引用,然后在提交的时候通过inputRef.current.value来获取输入的值。
三、createPortal:把组件“传送”到任意位置
createPortal是React提供的一个很有用的功能,它有点像Vue里的teleport。你可以用它把一个组件渲染到DOM的任意位置,而不是局限于父组件的DOM结构中。这在处理一些全局组件(如弹窗、下拉框、全局提示等)时非常方便。
import React from 'react';
import ReactDOM from 'react-dom';
const Modal = () => {
return ReactDOM.createPortal(
<div className="modal">
<p>这是一个模态框</p>
</div>,
document.body
);
};
在这个例子中,Modal组件通过createPortal被渲染到了document.body中,而不是它原本的位置。这样就可以让模态框脱离父组件的样式限制,更好地实现全局覆盖的效果。
四、Suspense:异步渲染的“魔法”
在React 18和React 19中,Suspense是一个非常重要的特性,它可以让组件异步加载,提升应用的性能和用户体验。
1. 使用lazy和Suspense异步加载组件
你可以用React.lazy来定义一个异步组件,然后用Suspense来包裹它。Suspense的fallback属性可以指定一个占位组件,在异步组件加载的过程中显示。例如:
import React, { Suspense, lazy } from 'react';
const AsyncComponent = lazy(() => import('./components/Async'));
const App = () => {
return (
<Suspense fallback={<div>Loading...</div>}>
<AsyncComponent />
</Suspense>
);
};
在这个例子中,AsyncComponent是一个异步加载的组件,当它还没有加载完成的时候,会显示Loading...这个占位组件。
2. 骨架屏提升用户体验
骨架屏是一种很常见的占位组件,它可以让用户在数据加载的过程中看到一个类似真实内容的“骨架”,而不是一个简单的加载动画。通过Suspense和骨架屏组件,你可以实现一个非常流畅的加载效果。例如:
import React, { Suspense, lazy } from 'react';
const Card = lazy(() => import('./components/Card'));
const Skeleton = () => <div className="skeleton">Loading...</div>;
const App = () => {
return (
<Suspense fallback={<Skeleton />}>
<Card />
</Suspense>
);
};
在这个例子中,Card组件是异步加载的,当它还在加载的时候,会显示一个骨架屏组件Skeleton。一旦Card加载完成,骨架屏就会被替换掉,显示真实的内容。
总结
React组件的使用涉及到很多细节,包括组件通信、表单管理、组件渲染位置以及异步加载等。通过合理地使用这些特性,你可以构建出高性能、用户体验良好的React应用。 希望这篇文章能帮助你更好地理解和使用React组件!😎
参考文档: React docs