什么是组件?
React 组件是 React 应用的基本构建块,它允许你将 UI 拆分成独立的、可复用的部分,并对每个部分进行独立的思考。组件就像函数一样,接收任意的输入(称为 “props”),并返回用于描述页面展示内容的 React 元素。组件分为函数组件和类组件,因为类组件已经基本弃用所以本文的组件就指函数组件。
函数组件示例
下面是一个简单的函数组件示例:
import React from 'react';
// 函数组件:欢迎消息
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
// 使用箭头函数的组件
const Greeting = (props) => {
return <p>Welcome, {props.username}!</p>;
};
// 组件可以嵌套使用
function App() {
return (
<div>
<Welcome name="Alice" />
<Greeting username="Bob" />
</div>
);
}
1. 父组件传子组件
父组件直接在子组件标签上写属性,子组件通过
props接收
在上面的例子中父组件App直接在子组件标签上传递属性name和username,子组件通过props接收,props(全称为 "properties")是一种从父组件向子组件传递数据的机制,类似于函数的参数。
props 的核心特点
- 只读性:props 是不可变的(immutable),子组件不能直接修改接收到的 props。
- 单向数据流:数据流动是单向的(从父组件到子组件)。
- 任意数据类型:props 可以传递任何数据类型(字符串、数字、对象、函数、甚至组件)。
- 组件复用性:通过 props 传递不同的数据,可以复用同一个组件。
props为对象,利用解构语法代码可以简化为
// 函数组件:欢迎消息
function Welcome({name}) {
return <h1>Hello, {name}</h1>;
}
2. 子组件传父组件
父组件创建一个函数,子组件调用这个函数,并将需要的数据作为参数传递
父组件
传递给子组件setList()函数,需要获得input框输入的值并展示在列表中
import { useState} from 'react'
import Child from './Child.jsx'
export default function Parent() {
const [list, setList] = useState(['html', 'css', 'js'])
return (
<div>
<Child setList={setList}/>
<div className="bd">
<ul>
{
list.map((item) => {
return <li key={item}>{item}</li>
})
}
</ul>
</div>
</div>
)
}
子组件
调用setList()函数,并将input框获得的值作为参数传递
import { useRef } from 'react'
export default function Child({ setList }) {
const inputRef = useRef(null)
const handler = () => {
// 将 input 中的值添加到父组件的 list 中
setList((preList) => {
return [...preList, inputRef.current.value]
})
}
return (
<div className="hd">
<input type="text" ref={inputRef}/>
<button onClick={handler}>确认</button>
</div>
)
}
3. 兄弟组件之间
子传父,父传子
结合前面两种方法,先让一个兄弟传递给它们的父组件然后父组件传给另一个兄弟
兄弟组件1
传递给父组件input框输入的内容
import React from 'react'
import { useRef} from 'react'
export default function Child1({setList}) {
const inputRef = useRef(null)
const handler = () => {
setList((preList)=>{
return [...preList,inputRef.current.value]
})
}
return (
<div className="hd">
<input type="text" ref={inputRef}/>
<button onClick={handler}>确认</button>
</div>
)
}
父组件
定义setList()函数,通过setList()得到修改的list,传递list给兄弟组件2
import Child1 from './Child1'
import Child2 from './Child2'
import { useState } from 'react'
export default function Parent() {
const [list, setList] = useState([])
return (
<div>
<Child1 setList={setList}/>
<Child2 list={list}/>
</div>
)
}
兄弟组件2
接收到list并渲染在li标签中
export default function Child2({list}) {
return (
<div className="bd">
<ul>
{
list.map((item) => {
return <li key={item}>{item}</li>
})
}
</ul>
</div>
)
}
4. 跨层级组件
使用useContext()钩子函数
import { useState, createContext, useContext } from "react"
const myConText = createContext()
function B() {
const msg = useContext(myConText)
return <div>
<h3>BBBB --- {msg}</h3>
</div>
}
function A() {
const msg = useContext(myConText)
return <div>
<h2>AAAA --- {msg}</h2>
<B />
</div>
}
export default function index() {
const [msg, setMsg] = useState("Index 组件中的数据")
return (
<div>
<myConText.Provider value={msg}>
<A />
</myConText.Provider>
</div>
)
}
使用方法可以看之前介绍钩子函数的文章--- React常用Hooks函数(1):useState、useEffect、useLayoutEffect……什么是 Ho - 掘金
5. 任意组件
想要实现在任意组件间通信有很多方法,这里介绍使用状态管理仓库的方法,在仓库中储存需要传递的信息,其它组件需要信息就从仓库中取。
状态管理库(Redux/MobX/ Zustand)
原理:集中管理应用的状态,组件通过订阅和派发操作共享状态。
适用场景:大型应用的复杂状态管理。
Redux 示例
1. 安装依赖
npm install @reduxjs/toolkit react-redux
2. 创建仓库
创建一个总仓库store目录,在总仓库index.js中注册子模块,子模块创建在modules目录的文件如counterStore.js中
└── store
├── modules
│ └── counterStore.js
└── index.js
index.js
//总仓库
import {configureStore} from "@reduxjs/toolkit";
import counterReducer from "./modules/counterStore.js";
export default configureStore({
reducer:{ //注册子模块
counter:counterReducer
}
})
counterStore.js
import { createSlice } from "@reduxjs/toolkit";
//仓库子模块
const counter = createSlice({
name: "counter",
initialState: {
list:["css"]
},
reducers: {
addList(state,action){
state.list.push(action.payload)
}
},
});
const { addList } = counter.actions;
export { addList };
const counterReducer = counter.reducer;
export default counterReducer;
3. 在mian.js引入
import { createRoot } from 'react-dom/client'
import App from './App.jsx'
import store from './store/index.js'
import { Provider } from 'react-redux'
createRoot(document.getElementById('root')).render(
<Provider store={store}>
<App />
</Provider>
)
4. 使用数据和方法
使用useSelector()和useDispatch()获取仓库中的数据和触发器
修改仓库数据:
- 调用仓库方法,得到一个action行为
- 调用dispatch将action行为传递给仓库
import { useSelector ,useDispatch} from 'react-redux'
import {useRef} from 'react'
import {addList} from '../../store/modules/counterStore.js'
export default function index() {
const inputRef = useRef(null);
//获取仓库中的数据
const {list} = useSelector((state)=>{
return state.counter
})
//修改仓库数据
const dispatch = useDispatch()
const addCount = () => {
const val=inputRef.current.value
dispatch(addList(val))
}
return (
<div>
<input type="text" ref={inputRef}/>
<button onClick={()=>{addCount()}}>添加</button>
<ul>
{
list.map((item,index)=>{
return <li key={index}>{item}</li>
})
}
</ul>
</div>
)
}
Redux的使用过于繁琐,接下来介绍一种更简单、易上手的方法状态管理库Zustand
zustand 示例
安装依赖
npm install zustand
在store文件夹下创建components.tsx文件,定义需要在任意组件使用的数据和方法并导出
import {create} from 'zustand'
const useComponentsStore = create((set) => ({
// 数据
mode: 'edit',
// 方法
setMode: (type:any) => set(() => ({ mode :type}))
}))
export default useComponentsStore
引入仓库中的数据和方法,实现点击按钮从预览变成退出预览
import { Button } from "antd"
import useComponentsStore from "../../stores/components.tsx"
export default function Materail() {
const mode = useComponentsStore((state: any) => state.mode)
return (
<div>
{mode === 'edit' && (
<Button type="primary" onClick={() => {
useComponentsStore.setState({ mode: 'preview' })
}}>预览</Button>
)}
{mode === 'preview' && (
<Button type="primary" onClick={() => {
useComponentsStore.setState({ mode: 'edit' })
}}>退出预览</Button>
)}
</div>
)
}