一、使用CRA创建项目
创建项目命令:npx create-react-app 项目名称。如:npx create-react-app react-basic
启动:npm run start
如果太慢切换最新的淘宝镜像:npm config set registry https://registry.npmmirror.com
查看镜像源: npm config get registry
二、React 事件绑定
语法:on + 事件名称 = { 事件处理程序 },整体上遵循驼峰命名法
如:
function App() {
const setConsoleBtn=()=>{
console.log('打印')
}
const setConsoleBtn2=()=>{
console.log('打印2')
}
const setConsoleBtn3=()=>{
console.log('打印3')
}
return (
<div className="App">
<span></span>
<button onClick={() => setConsoleBtn()}>打印</button>
<button onClick={ setConsoleBtn2()}>打印2</button>
<button onClick={ setConsoleBtn3}>打印3</button>
</div>
);
}
export default App;
注意3个写法区别:打印按钮和打印3按钮被点击时执行,打印2页面渲染后立马执行。
三、useState 函数
导入:import { useState } from 'react'
语法:const [存储状态的变量,修改状态的函数]=useState(默认值)
如:
import { useState } from 'react'
function App() {
const [count, setCount] = useState(1)
const [counter, setCounter] = useState({ a: 1, b: 2 })
// 此时counter的值被改为了 { b: 4 }, 而不是 { a: 1, b: 4 },setCounter({ b: 4 });
// 如果想要得到 { a: 1, b: 4 }的结果,就必须这样,setCounter({ ...counter, b: 4 });
return (
<div className="App">
<span>{count}</span>
<button onClick={() => setCount(10)}>增加到10</button>
<button onClick={() => setCount(count + 1)}>点击+1</button>
<span>a:{counter.a},b{counter.b}</span>
<button onClick={() => setCounter({ ...counter, b: 4 })}>改变</button>
</div>
);
}
export default App;
修改状态的规则:
规则:不要直接修改当前状态的值,应该创建新值。
简单类型:如:
const [count, setCount] = useState(1)
不要:
count+1
setCount(count)
而要:setCount(count + 1)
复杂类型:如: const [list,setList]=useState(['苹果'])
不要:list.push('梨子') setList(list)
而要:setList(...list,"梨子")
使用状态操作表单元素的值:
const [value,setValue]=useState("")
<input value={value} onChange={e=>setValue(e.target.value)}>
四、使用 classnames 优化类名处理
装包:npm i classname
导包:import classNames from 'classnames'
1.基础用法:
classNames('foo', 'bar'); // => 'foo bar'
2.条件用法:
classNames('foo', { 'bar': true, 'duck': false }); // => 'foo bar'
3.多条件用法:
const buttonType = 'primary';
classNames({
'btn-default': buttonType === 'default',
'btn-primary': buttonType === 'primary'
}); // => 'btn-primary'
4.数组用法:
const buttonTypes = ['primary', 'bold'];
classNames(buttonTypes); // => 'primary bold'
如react组件中使用:
import classNames from 'classnames';
const Button = ({ primary, children }) => (
<button className={classNames('btn', { 'btn-primary': primary })}>
{children}
</button>
);
// 使用 <Button primary>Click me</Button> 时
// 结果的 className 将是 'btn btn-primary'
五、useRef 与 DOM 操作
1.导入:import { useRef } from 'react'
2.定义:
const inputRef = useRef(null)
<input ref={inputRef} />
3.根据相关业务进行dom操作:
inputRef.current.value
inputRef.current.focus()
import { useRef } from 'react'
const App = () => {
const inputRef = useRef(null)
// 注意:不要在组件渲染时,使用 ref 进行 DOM 操作
console.log(inputRef.current)
return (
<div>
<input ref={inputRef} />
<hr />
<button onClick={() => console.log(inputRef.current.value)}>
获取文本框的值
</button>
<button onClick={() => inputRef.current.focus()}>获得焦点</button>
</div>
)
}
export default App
六、组件之间的通讯
1.父子之间的通讯
父组件提供数据,通过 props 传递给子组件使用。
父组件准备修改数据的函数,传递给子组件,子组件调用函数,将数据作为参数回传给父组件。
import { useState } from 'react'
// 子组件
const Son = ({ data,changeData }) => {
return (
<div >
<div >{data}</div>
<button onClick={changeData}>切换</button>
</div>
)
}
// 父组件
const App = () => {
const [str, setStr] = useState("Hello, World!")
const handleClick = () => {
setStr('改变str')
};
return (
<div className="app">
<Son data={str} changeData={handleClick}></Son>
</div>
)
}
export default App
2.兄弟组件通讯
如果两个兄弟组件要通讯,就把共享数据提升到公共父组件 中
import { useState } from 'react'
// 兄弟组件1
const Friends1 = ({ data }) => {
return (
<div >
<div >{data}</div>
</div>
)
}
// 兄弟组件2
const Friends2 = ({changeData}) => {
return (
<div >
<button onClick={changeData}>改变</button>
</div>
)
}
// 父组件
const App = () => {
// 1. 找到父组件,提供要共享的数据
const [str, setStr] = useState("我是兄弟1里面的数据")
const handleClick = () => {
setStr('改变兄弟的数据,你变了')
};
return (
<div className="app">
{/* 2. 通过父到子通讯,来展示信息称 */}
<Friends1 data={str} ></Friends1>
{/* 3. 通过子到父通讯,来修改选兄弟组件的数据 */}
<Friends2 changeData={handleClick}></Friends2>
</div>
)
}
export default App
3.跨组件通讯,使用
(1)导入useContext,createContext
import { useContext } from 'react'
import { createContext } from 'react'
(2)获取共享数据
const { 共享数据 } = useContext(ThemeContext)
(3)划定范围,提供共享数据.使用ThemeContext.Provider进行包裹,value里属性为共享的数据
<ThemeContext.Provider value={{共享数据}} >
...其他组件
</ThemeContext.Provider>
import { useContext, useState } from 'react'
import { createContext } from 'react'
const ThemeContext = createContext()
//子组件
const Son = () => {
const { num } = useContext(ThemeContext)
return (
<span>{num}</span>
)
}
const Father = () => {
return (
<div >
<Son />
</div>
)
}
//跨组件
const Cross = () => {
const { onNum } = useContext(ThemeContext)
return (
<button onClick={onNum}>改变跨组件的数字</button>
)
}
// 父组件
const App = () => {
const [num, setNum] = useState(111)
const onNum = () => {
setNum(num+1)
}
return (
<div className="app">
<ThemeContext.Provider value={{ num, onNum }} >
<Father >
</Father>
<Cross>
</Cross>
</ThemeContext.Provider>
</div>
)
}
export default App
七、useEffect 的使用
useEffect 的作用:在组件生命周期的三个阶段(挂载、更新、卸载),执行网络请求、浏览器 API 等操作 这些操作,也叫:副作用(side effects)
1.挂载时,页面一加载就调用
import { useEffect } from 'react'
const App = () => {
useEffect(() => {
console.log('页面加载了,相当于vue的mounted')
}, [])
return (
<div className="app">
</div>
)
}
export default App
2.数据更新时候
useEffect(() => { 操作 }, [更新的变量])
import { useEffect } from 'react'
import { useState } from 'react'
const App = () => {
const [num, setNum] = useState(1)
useEffect(() => {
console.log('num更新了')
}, [num])
return (
<div className="app">
<div>{num} </div>
<button onClick={() => setNum(num + 1)}>更新</button>
</div>
)
}
export default App
3.卸载时
语法: useEffect(() => { return 操作 }, [更新的变量]) return即可
注意:useEffect是卸载组件里面,不可卸载外面
import { useEffect } from 'react'
import { useState } from 'react'
const Comp = () => {
useEffect(() => {
return console.log('我卸载了')
}, [])
return (
<div >
我是组件
</div>
)
}
const App = () => {
const [flag, setFlag] = useState(true)
return (
<div className="app">
<button onClick={() => setFlag(!flag)}>{flag ? "卸载组件" : "开启组件"}</button>
{flag ? (<Comp></Comp>):(<div>点击开启组件开启吧</div>)}
</div>
)
}
export default App
4.useEffect里面发请求
应当这样:在里面写个函数再调用
const [data, setData] = useState([])
useEffect(() => {
const loadData = async () => {
const res = await axios.get('http://localhost:8080/data')
setData(res.data)
}
loadData()
}, [])
不可以这样:
const [data, setData] = useState([])
useEffect(async () => {
const res = await axios.get('http://localhost:8080/todos')
setData(res.data)
}, [])
注意:不要直接在 Effect函数 上添加 async ,因为 Effect 函数是同步的
八、Redux
Redux 是React最常用的集中状态管理工具,类似于Vue中的Pinia(Vuex)
使用步骤:
1.下载相关包
npm i @reduxjs/toolkit react-redux
2.处理相关,如store.js
import { configureStore, createSlice } from "@reduxjs/toolkit"
const countStore = createSlice({
name: 'count',
initialState: {//数据
count: 1,
// list:[]
},
reducers: { //处理数据函数
inscrement(state) {
state.count++
},
decrement(state) {
state.count--
},
setCount(state, action) {
state.count = action.payload
},
// setLists(state, action) {
// state.list = action.payload
// }
}
})
const store = configureStore({
reducer: {
counter: countStore.reducer
//...多个,进行模块化处理
}
})
//异步请求可在这里写,或者组件中也可以,如:
// const fetchList = () => {
// return async (dispatch) => {
// const res = await axios.get('http://127.0.0.1:8888/data')
// dispatch(channelStore.actions.setLists(res.data.data))
// }
// }
// export { fetchList }
const { inscrement, decrement, setCount } = countStore.actions
export { inscrement, decrement, setCount }
export default store
一般将里面的configureStore抽离出去,进行模块化处理。
3.index.js中注入store
...省略其他
import store from './store'
import { Provider } from 'react-redux'
root.render(
<Provider store={store}>
<App />
</Provider>
);
4.组件中使用
import { useDispatch, useSelector } from 'react-redux'
import { inscrement, decrement, setCount } from './store'
const App = () => {
const { count } = useSelector(state => state.counter)
const dispatch = useDispatch()
return (
<div className="app">
<button onClick={() => dispatch(decrement())}>-</button>
{count}
<button onClick={() => dispatch(inscrement())}>+</button>
<button onClick={() => dispatch(setCount(10))}>add To 10</button>
<button onClick={() => dispatch(setCount(20))}>add To 20</button>
</div>
)
}
export default App
九、路由
1.定义
import Layout from '@/pages/Layout'
import Month from '@/pages/Month'
import New from '@/pages/New'
import Year from '@/pages/Year'
import { createBrowserRouter } from 'react-router-dom'
const router = createBrowserRouter([
{
path: '/',
element: <Layout />,
children: [
{
path: 'month',
element: <Month />
},
{
path: 'year',
element: <Year />
}
]
},
{
path: '/new',
element: <New />
}
])
export default router
2.使用
(1)声明式导航
通过 <Link/>
组件描述出要跳转到哪里去
import { Link } from "react-router-dom"
<Link to="/about">关于</Link>
语法说明:通过给组件的to属性指定要跳转到路由path,组件会被渲染为浏览器支持的a链接,如果需要传参直接通过字符串拼接的方式拼接参数即可
(2)编程式导航
通过 useNavigate
钩子得到导航方法,然后通过调用方法以命令式的形式进行路由跳转
import { useNavigate } from 'react-router-dom'
<button onClick={() => navigate('/article')}>跳转到文章页</button>
<button onClick={() => navigate('/article?id=1001&name=jack')}>searchParams传参</button>
<button onClick={() => navigate('/article/1001/jack')}>params传参</button>
2种传参方式:
1.searchParams传参
navigate('/article?id=1001&name=jack')
路由写法,正常写:
{
path: '/article',
element: <Article/>
},
对应组件接收:
import { useSearchParams } from "react-router-dom"
const [params] = useSearchParams()
const id = params.get('id')
const name = params.get('name')
2.params传参
/article/1001/jack
路由写法:
{
path: '/article/:id/:name',
element: <Article />
},
对应组件接收:
import { useParams} from "react-router-dom"
const params = useParams()
const id = params.id
const name = params.name
嵌套路由配置:
在一级路由中又内嵌了其他路由,这种关系就叫做嵌套路由,二级、三级等
实现步骤
-
使用
children
属性配置路由嵌套关系 -
使用
<Outlet/>
组件配置二级路由渲染位置
路由中配置:
{
path: '/',
element: <Layout />,
children: [
// 设置为默认二级路由 一级路由访问的时候,它也能得到渲染。设置index: true默认访问
{
index: true,
element: <Board />
},
{
path: 'about',
element: <About />
}
]
},
组件中二级路由使用:
import { Link, Outlet } from "react-router-dom"
<div>
我是一级路由layout组件
<Link to="/">面板</Link>
<Link to="/about">关于</Link>
{/* 配置二级路由的出口 */}
<Outlet /> {/* 跳转的路由显示在这里 */}
</div>