目录
(1)npm i react@17.0.0 react-dom@17.0.0 babel-standalone@6.26.0
2、方法二:create React App脚手架工具创建项目
开始一个React项目(一)一个最简单的webpack配置_dengdengda的博客-优快云博客
(1)注意事项:原生写法onClick中C要大写;事件处理函数名是个变量,用jsx写法;
7、Fragments 短语法:表示根元素,渲染后不会编译成其他元素
*state可定义在constructor(){}内,也可以不定义在里面
*setState()是异步操作,若要等值更改好,在调用可使用promise方法包裹(例如商品列表加载更多)
注意:在constructor(){}里面定义变量,改变this指向
注意:如果回调函数通过props传入子组件时,这些组件可能会进行重新渲染
注意:不传参时,绑定事件不用套一层箭头函数,传参时,绑定事件要套一层箭头函数
(2)src文件夹下创建utils文件夹,utils文件夹内创建utils.js文件,文件内创建Context对象,导出Provider和Consumer对象
(3)在index.js文件中引入utils.js暴露的Provider,Provider包裹根组件,并定义要传递的内容
(4)下层组件中引入utils.js中暴露的Consumer,接收根组件传递的值
6、Refs(允许访问DOM节点或render方法中创建的React元素-子组件)
(1)在类组件内部创建Refs对象,可多次创建,且只能在类组件中创建
1、网址:React lifecycle methods diagram
*app.js文件内App组件不要用严格模式包裹,否则初始化时,componentDidMount会执行2次
(1)一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数
(2)常用的Hooks:setState() setEffect() setRef() setContext()
(2)将组件中相互关联的部分拆分成更小的函数(比如设置订阅或请求数据),而并非强制按照生命周期划分
(3)不编写 class 的情况下使用 state 以及其他的 React 特性
(2)语法(useState内可定义数组、对象、数值、字符串等数据类型)
*副作用:函数组件主要作用是通过数据渲染UI界面,除了这个之外的操作就是副作用,例如:ajax请求、手动修改dom、 localstorage操作等
*方式一:useEffect(()=>{}) //第一次进入执行,每次更新都会执行
*方法二:useEffect(()=>{},[]) // 第一次进入时执行
*方法三:useEffect(()=>{},[count]) //第一次进入时执行,或者count变化,执行回调函数(类似watch监听器)
*方法四:useEffect(()=>{ return ()=>{} }) // 组件销毁执行return中代码(可以加[],可以不加[])
(1)utils.js中创建MessageContext实例对象
(1)作用:缓存函数,只执行一次,可解决React.memo中子组件接收函数而父组件渲染,子组件跟着一起渲染问题
2、React.memo高阶组件(只有组件内状态变化才执行)
*若父组件传给子组件一个函数,父组件渲染,子组件跟着一起渲染;
*原因:函数是复杂数据类型,父组件更新,重新生成新的函数对象
八、fetch(内置网络请求方法,直接使用,get方法可以不写出来)
npm install react-router-dom --save
(2)路由模式:HashRouter 和 BrowserRouter
(1)作用:通过js编程的方式进行路由页面跳转,比如登录页到关于页
*执行跳转函数完成跳转(默认是push方式跳转,可定义replace方式)
(1)css命名为index.module.css //主要是要带module字段
(2)index.js中引入样式文件 import styles from ’css文件路径‘
(2) Store : Store 就是保存数据的地方,你可以把它看成一个容器。整个应用只能有一个 Store。
(5) dispatch:store操作行为触发方法,是执行action的方法。 store.dispatch(‘action’, payload)
(7) getState(): store.getState()获取state状态数据
(1)新建store文件夹,文件夹下新建store,js文件
*改变值:store.dispatch({type:'minus'} //dispatch中的对象是action对象
5、combineReducers(合并Reducers、适用于store.js里面有多个reducers时)
(1)使用:const reducer = combineReducers({counterReducer,cartReducer})
(2)组件中取值:let list = store.getState().cartReducer.list
(2)reducer函数中操作state,先拷贝一份,更改之后返回一个新state
十五、Redux Toolkit库(可以直接更改state内的值,不用拷贝一份再修改)
(2)store文件夹下定义的index.js文件内:集成多个reducers
*注意:需要使用store.subscribe()去监听state内值的变化,然后使用useState()去更改值,从而刷新界面
十六、React-Redux库(使用hooks函数简化组件内取值和改变值的操作)
2、在index.js文件中导入Provider包裹App组件,还要导入暴露的store(让useSelector()与store建立联系,Provider透传store)
3、组件内导入useSelector,useDispatch
(1)注意:useSelector()会监听数据变化从而刷新界面,不用useState()更改值在刷新界面
(2)import { useSelector,useDispatch } from 'react-redux'
const cartList= useSelector(state=>state.cart.list)
const dispatch = useDispatch()
十七、Redux中进行异步任务操作(必须是在安装了Redux Toolkit库才行)
(1)引入createAsyncThunk(),创建对象,并暴露出去,在组件中使用(系统会自动封装成promise,返回三种状态:成功、加载、失败)
(2) extraReducers(builder){ }中获取异步任务返回的值,进行保存
(1)引入createAsyncThunk()所创建的暴露了的对象
(2)使用useDispatch进行传参(传入异步任务所返回的值)
一、React介绍
1、特点:数据驱动、组件化、虚拟DOM
二、开发环境
1、方法一:引入js
(1)npm i react@17.0.0 react-dom@17.0.0 babel-standalone@6.26.0
(2)按顺序引入下面三个js文件
2、方法二:create React App脚手架工具创建项目
(1)安装集成环境
*安装插件
*编写代码rcc 就可以生成有状态的组件代码块
(2)创建项目和运行
npx create-react-app my-app
cd my-app
npm start
3、方法三:webpack手动搭建
开始一个React项目(一)一个最简单的webpack配置_dengdengda的博客-优快云博客
三、核心基础
1、用法
<div id="app">
</div>
<script type="text/babel">
// ReactDOM render(react元素,根节点)
ReactDOM.render(<h2>helloworld</h2>,document.getElementById('app'))
</script>
2、jsx
(1)含义:
JSX 全称 JavaScript XML ,是一种扩展的 JavaScript 语言, 它允许 HTML 语言直接写在 JavaScript 语言中,不加任何引号,这就是 JSX 语法。 它允许 HTML 与 JavaScript 的混写。
(2)规则:
- {} 内容做为javascript代码解析
- () 作用html标签解析 - 必须有根节点 - 单标签不能省略结束标签。/>
<img src=" " alt=" " />
- 必须有根节点
<div>
<p>{props.user.name}</p>
<p>{props.user.age}</p>
<p>{props.title}</p>
</div>
- JSX 允许直接在模板中插入一个 JavaScript 表达式(如:三目运算)
3、操作属性
const url="路径"
<img src={url} />
4、注释
{/*注释:操作行内样式*/}
5、操作样式
(1)行内样式
const sty = {
color: 'red',
fontSize:'28px'
}
<p style={ {color:'blue'} }> 样式简写 </p>
<h2 style={sty}>样式操作示例</h2>
(2)类样式
<style>
.m-style{
width: 100px;
height: 100px;
background-color: skyblue;
}
</style>
<p className="m-style">类样式</p>
6、操作事件
(1)注意事项:原生写法onClick中C要大写;事件处理函数名是个变量,用jsx写法;
function bindClick(){
console.log('触发事件');
}
{/* 操作事件 */}
<button type="button" onClick={bindClick}>按钮</button>
7、Fragments 短语法:表示根元素,渲染后不会编译成其他元素
<React.Fragment>
{/* 操作内容 */}
<h2>{title}</h2>
</React.Fragment>
8、条件渲染
(1)注意事项:复杂的if语句,封装成函数,调用函数
<div id="app">
</div>
<script type="text/babel">
let age = 19
function getAgeMessage() {
if (age > 18) {
return <h2>成年</h2>
} else if (age == 18) {
return <h2>18岁</h2>
} else {
return <h2>未成年</h2>
}
}
const element = (
<div>
{age > 18 ? '成年人' : '未成年'}
{age > 18 ? (<h2>成年人</h2>) : (<h2>未成年人</h2>)}
{age > 18 && <p>逻辑与条件处理</p>}
{getAgeMessage()}
</div>
)
ReactDOM.render(element, document.getElementById('app'))
</script>
9、列表渲染(map方法)
const list = [
{ id: 1001, name: 'javascrip编程', price: 18.89 },
{ id: 1002, name: 'vue编程', price: 58.89 },
{ id: 1003, name: 'react编程', price: 98.89 },
]
// let arr = list.map(item=><li>{item.id} {item.name} {item.price}</li>)
const element = (
<div>
<ul>
{/*
<li>1001 javascrip编程 18.89</li>
<li>1002 javascrip编程 18.89</li>
<li>1003 javascrip编程 18.89</li>*/}
{list.map(item => <li>{item.id} {item.name} {item.price}</li>)}
</ul>
<table className="y-table">
{
list.map((item,index) => (
<tr key={index}>
<td>{item.id}</td>
<td>{item.name}</td>
<td>{item.price}</td>
</tr>
))
}
</table>
</div>
)
10、组件和props
(1)函数组件
function Welcome(props) {
return (
<div>
<h2>组件</h2>
<p>{props.title}</p>
<p>{props.content}</p>
</div>
)
}
(2)类组件(props 作为类对象子对象存在)
class WelcomeTwo extends React.Component {
constructor(){
super()
}
render() {
return (
<div>
<h2>组件</h2>
<p>{this.props.title}</p>
</div>
)
}
}
(3)使用
ReactDOM.render(<WelcomeTwo title="类组件" content="这是函数组件学习"/>, document.getElementById('app'))
11、store(操作组件内部数据)
(1)类组件
注意:
*state可定义在constructor(){}内,也可以不定义在里面
*setState()是异步操作,若要等值更改好,在调用可使用promise方法包裹(例如商品列表加载更多)
class WelcomeTwo extends React.Component {
// constructor(){
// super()
// }
//定义组件内部状态数据
state = {
user: {
name: 'jack',
age: 18
},
content: '这是组件内部数据'
}
render() {
return (
<div>
<h2>组件</h2>
<p>{this.props.title}</p>
<p>{this.state.content}</p>
<p>{this.state.user.name}</p>
</div>
)
}
}
12、类组件事件和this指向
(1)方法一:通过.bind改变this指向
{/*事件处理函数中绑定this指向*/}
<button onClick={this.onConfirm.bind(this)}>确定</button>
onConfirm() {
let value = this.state.inputValue
console.log('onConfirm >>>', value);
}
(2)方法二:
注意:在constructor(){}里面定义变量,改变this指向
constructor() {
super()
this.state = {
inputValue: 'javascript高级编程',
count: 0
}
// 绑定this
this.onPlus = this.onPlus.bind(this)
}
{/* 事件绑定,初始化时绑定this */}
<button onClick={this.onPlus}>加一</button>
onPlus() {
console.log('plus ', this.state.count);
}
(3)方法三(箭头函数)
注意:如果回调函数通过props传入子组件时,这些组件可能会进行重新渲染
{/* 箭头函数实现 */}
<button onClick={ ()=>{this.onMinus(100)} }>减一</button>
onMinus(age) {
console.log('age ',age);
console.log('minus ', this.state.count);
}
(4)方法四(事件处理函数定义为箭头函数,推荐使用)
注意:不传参时,绑定事件不用套一层箭头函数,传参时,绑定事件要套一层箭头函数
{/* 事件绑定写法 */}
<button onClick={()=>{this.onDouble(200)}}>双倍</button>
onDouble = (age)=>{
console.log('age ',age);
console.log('onDouble ',this.state.count);
}
13、类组件改变状态值(类似于微信小程序原生写法)
plus=()=>{
let count = this.state.count
count++
this.setState({
count
})
}
14、阻止事件默认行为
event.preventDefault()
四、组件通讯
1、props
(1)值只读,不能修改
(2)任意类型数据可传(字符串、jsx、数组、对象等)
2、插槽概念
(1)父组件
<SonA emit={<p>这是jsx参数</p>}/>
(2)子组件
{props.emit}
3、子传父
(1)父组件
<SonA emit={ (msg)=>{this.getMessage(msg)} }/>
getMessage = (msg)=>{
console.log('函数参数getMessage',msg)
}
(2)子组件
<button type="button" onClick={ ()=>{props.emit('这是子组件参数')} }>确定</button>
4、兄弟之间
子组件A—父组件—子组件B
5、跨组件通讯(App组件向任意一个下层组件传递数据)
(1)利用Context实现
(2)src文件夹下创建utils文件夹,utils文件夹内创建utils.js文件,文件内创建Context对象,导出Provider和Consumer对象
// 创建Context对象,导出Provider和Consumer对象
import React from 'react'
export const {Provider,Consumer} = React.createContext()
(3)在index.js文件中引入utils.js暴露的Provider,Provider包裹根组件,并定义要传递的内容
root.render(
<Provider value="这是根数据">
<App />
</Provider>
)
(4)下层组件中引入utils.js中暴露的Consumer,接收根组件传递的值
<Consumer>{ (value)=><p>{value}</p> }</Consumer>
6、Refs(允许访问DOM节点或render方法中创建的React元素-子组件)
(1)在类组件内部创建Refs对象,可多次创建,且只能在类组件中创建
export default class ParentRefs extends Component {
constructor(){
super()
this.state={
message:''
}
// this.myRef = React.createRef() // 1.创建myRef实例对象
this.sonRef = React.createRef()
}
render() {
return (
<div>
<h2>refs获取组件实例或Dom节点</h2>
{this.state.message}
{/* <div id="d1" ref={this.myRef} >refs操作dom节点</div> */}
<SonA ref={this.sonRef}/>
<button type="button" onClick={this.onConfirm}>
确定
</button>
</div>
)
}
onConfirm = () => {
// 获取dom节点内容
// const divEle = document.getElementById('d1')
// divEle.innerHTML = '这是新值'
// const divEle = this.myRef.current
// divEle.innerHTML = '这是refs改变值'
const sonA = this.sonRef.current
console.log('sonA ',sonA);
this.setState({message:sonA.state.content})
}
}
五、生命周期
1、网址:React lifecycle methods diagram
2、生命周期视图
3、生命周期钩子函数的触发时间和作用
(1)挂载阶段
注意事项:
*app.js文件内App组件不要用严格模式<React.StrictMode></React.StrictMode>包裹,否则初始化时,componentDidMount会执行2次
(2)更新阶段
(3)卸载阶段
六、Hooks(只能在函数组件中使用)
1、定义
(1)一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数
(2)常用的Hooks:setState() setEffect() setRef() setContext()
2、特点
(1)在组件之间复用状态逻辑, 无需修改组件结构
(2)将组件中相互关联的部分拆分成更小的函数(比如设置订阅或请求数据),而并非强制按照生命周期划分
(3)不编写 class 的情况下使用 state 以及其他的 React 特性
3、useState
(1)使用
import React, { useEffect, useState } from 'react'
(2)语法(useState内可定义数组、对象、数值、字符串等数据类型)
const [count,setCount] = useState(0)
const [user,setUser] = useState({name:'jack',age:18})
4、useEffect
(1)定义:
*可以让在函数组件中执行副作用操作
*副作用:函数组件主要作用是通过数据渲染UI界面,除了这个之外的操作就是副作用,例如:ajax请求、手动修改dom、 localstorage操作等
(2)语法:
*方式一:useEffect(()=>{}) //第一次进入执行,每次更新都会执行
*方法二:useEffect(()=>{},[]) // 第一次进入时执行
*方法三:useEffect(()=>{},[count]) //第一次进入时执行,或者count变化,执行回调函数(类似watch监听器)
*方法四:useEffect(()=>{ return ()=>{} }) // 组件销毁执行return中代码(可以加[],可以不加[])
5、useContext(跨组件通讯)
(1)utils.js中创建MessageContext实例对象
export const MessageContext = React.createContext()
(2)App.js中引入MessageContext
import { MessageContext } from '../utils/util'
<MessageContext.Provider value='测试'>
<Root />
</MessageContext.Provider>
(3)下层组件中引入MessageContext
const message = useContext(MessageContext)
<p>{message}</p>
6、useRef
const inputRef = useRef(null) //创建ref对象
const setFocus = ()=>{
inputRef.current.focus()
}
return (
<div>
<h2>TextInputFocus</h2>
<input type="text" ref={inputRef}/>
<button type="button" onClick={ setFocus }>获取焦点</button>
</div>
)
7、useMemo(性能优化)
(1)用法:依赖的值变化,才执行(且需要返回一个值时)
const [user, setUser] = useState({ gender: 0, age: 18 })
const formateGennder = gender => {
console.log('222222')
if (gender === 0) {
return '男'
} else {
return '女'
}
}
//使用useMemo优化
const gender = useMemo(() => {
return formateGennder(user.gender)
}, [user.gender])
//展示
<p>{gender}</p>
8、useCallback
(1)作用:缓存函数,只执行一次,可解决React.memo中子组件接收函数而父组件渲染,子组件跟着一起渲染问题
(2)使用
const setNum = useCallback(()=>{
console.log('函数参数');
},[])
七、高阶组件
1、定义
(1)是一个函数
(2)接收一个组件参数,返回一个新的组件
2、React.memo高阶组件(只有组件内状态变化才执行)
(1)使用方法(包裹子组件)
const 新组件=React.memo(组件)
import React, { memo } from 'react'
export default memo(function Son(props) {
console.log('son >>>>>')
return (
<div>
<h2>Son</h2>
{/* <p>{num}</p> */}
</div>
)
})
(2)使用场景:更新父组件时,子组件不更新
(3)组件更新的条件:setState、props
(4)注意事项:
*若父组件传给子组件一个函数,父组件渲染,子组件跟着一起渲染;
*原因:函数是复杂数据类型,父组件更新,重新生成新的函数对象
*解决方法:使用useCallback()包裹传递的函数
八、fetch(内置网络请求方法,直接使用,get方法可以不写出来)
fetch('后端网址url')
.then(res => {
return res.json()
})
.then(data => {
console.log('data ', data)
let { resultCode, resultInfo } = data
if (resultCode === 1) {
setList(resultInfo.list)
}
})
.catch(error => {
setError(error)
})
九、React路由创建
1、下载react-router
npm install react-router-dom --save
2、导出路由相关组件
(1)const{BrowserRouter、HashRouter、Routes、Route、NavLink、Navigate} from ’react-router-dom‘
(2)路由模式:HashRouter 和 BrowserRouter
(3)路由规则:Routes和Route
(4)类似a标签的跳转:NavLink
(5)重定向:Navigate
3、使用
<BrowserRouter>
{/* 点击跳转,类似a标签 */}
<NavLink to="/nba">Nba</NavLink> | <NavLink to="/news">新闻</NavLink>
{/* Routes 路由容器 */}
<Routes>
{/* Navigate 重定向 */}
<Route path="/" element={<Navigate to="/login"></Navigate>}></Route>
<Route path="/login" element={<Login/>}></Route>
<Route path="/home" element={<Home/>}></Route>
{/* 路由规则 Route path路径 element元素 */}
<Route path="/nba" element={<Nba />}></Route>
<Route path="/news" element={<News />}>
{/* 嵌套路由 index默认路由*/}
<Route index element={<Fun />}></Route>
<Route path="sports" element={<Sports />}></Route>
</Route>
{/* 路由找不到404处理 */}
<Route path="*" element={ <NotFund/>}></Route>
</Routes>
</BrowserRouter>
十、嵌套路由
1、定义子路由(路径不要有'/')
<BrowserRouter>
{/* Routes 路由容器 */}
<Routes>
<Route path="/news" element={<News />}>
{/* 嵌套路由 index默认路由*/}
<Route index element={<Fun />}></Route>
<Route path="sports" element={<Sports />}></Route>
</Route>
</Routes>
</BrowserRouter>
2、子路由展示:父路由对应组件内使用Outlet展示,若父组件内要定义NavLink跳转,则路径要加上父路由的路径
十一、全局匹配404
1、<Route path="*" element={ <NotFund/>}></Route>
十二、编程导航与传参
1、编程式导航-跳转
(1)作用:通过js编程的方式进行路由页面跳转,比如登录页到关于页
(2)语法
*导入useNavigate钩子函数
import { useNavigate } from 'react-router-dom'
*执行useNavigate钩子函数得到跳转函数
const navigate = useNavigate()
*执行跳转函数完成跳转(默认是push方式跳转,可定义replace方式)
navigate('/home?username=admin&password=123',{replace:true})
2、动态传参
(1)跳转时路径带参数
navigate('/home?username=admin&password=123',{replace:true})
(2)接收参数(useSearchParams())
import React from 'react'
import { useSearchParams } from 'react-router-dom'
export default function Home() {
const [params] = useSearchParams()
return (
<div>
<h3>主界面</h3>
{params.get('username')} - {params.get('password')}
</div>
)
}
十三、定义组件局部css样式
1、定义局部样式原因:组件的全局样式会互相影响
2、定义方法
(1)css命名为index.module.css //主要是要带module字段
(2)index.js中引入样式文件 import styles from ’css文件路径‘
(3)使用(两种方式)
<p className={styles['user-msg']}></p>
//第二种方式:如果classname命名不带'-',就可以直接使用下面的方式
<p className={styles.gUser}></p>
十四、高阶组件(定义移动端TabBar)
1、定义首页、分类、购物车、我的和TabBar组件
2、定义首页、分类、购物车、我的路由(App.js中)
(1)注意:传子组件的方式:<Tabbar><Category/></Tabbar>
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'
import Home from './components/Home'
import Category from './components/Category'
import Cart from './components/Cart'
import My from './components/My'
import Tabbar from './components/Tabbar/Index'
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={ <Navigate to="/home"/>}></Route>
<Route path="/home" element={ <Tabbar><Home/></Tabbar>}></Route>
<Route path="/category" element={ <Tabbar><Category/></Tabbar>}></Route>
<Route path="/cart" element={ <Tabbar><Cart/></Tabbar>}></Route>
<Route path="/my" element={ <Tabbar><My/></Tabbar>}></Route>
</Routes>
</BrowserRouter>
)
}
export default App
3、TabBar组件内定义高阶组件
import React, { useState } from 'react'
import {NavLink} from 'react-router-dom'
import styles from './index.module.scss'
import '../../assets/iconfont/iconfont.css'
export default function Index({children}) {
return <>{Tabbar(children)}</>
/**
* 高阶组件
* children
*
*/
function Tabbar(children) {
const [id,setId] = useState(0) // 当前索引号
const [list] = useState([
{ path: '/home', title: '首页', icon: 'icon-daohanglan-03' },
{ path: '/category', title: '分类', icon: 'icon-icon04' },
{ path: '/cart', title: '购物车', icon: 'icon-shipin' },
{ path: '/my', title: '我的', icon: 'icon-My' },
])
return (
<div className={styles['g_home']}>
<div className={styles.gMain}>
<div>{children}</div>
</div>
<div className={styles.gTabbar}>
<ul>
{list.map( (item,index)=>{
return (
<li>
<NavLink to={item.path} className={index === id?styles.active:''} onClick={()=>{ setId(index) }}>
<span className={'iconfont '+item.icon}></span>
<p >{item.title}</p>
</NavLink>
</li>
)
})}
</ul>
</div>
</div>
)
}
}
十五、Redux 状态管理
1、特点:
(1)集中式存储管理应用的所有组件的状态
(2)保证状态以一种可预测的方式发生变化
(3)简化Redux组件间通讯
2、核心概念与理论
(1)state: state对象包含所有数据。
(2) Store : Store 就是保存数据的地方,你可以把它看成一个容器。整个应用只能有一个 Store。
(3) Action : Action 就是 View 发出的通知,表示 State 应该要发生变化了, Action 是一个对象。其中的type属性是必须的,表示 Action 的名称。其他属性可以自由设置. 由组件store.dispatch(‘action’, payload)触发。
(4) Reducer: Store 收到 Action 以后,必须给出一个新的 State,这样 View 才会发生变化。这种 State 的计算过程就叫做 Reducer。(state,action)=> newState;
(5) dispatch:store操作行为触发方法,是执行action的方法。 store.dispatch(‘action’, payload)
(6)subscribe: Store 允许使用store.subscribe方法设置监听函数,一旦 State 发生变化,就自动执行这个函数。 只要把 View 的更新函数(对于 React 项目,就是组件的render方法或setState方法)放入listen,就会实现 View 的自动渲染。
(7) getState(): store.getState()获取state状态数据
3、安装
(1)npm install redux --save
4、基础使用
(1)新建store文件夹,文件夹下新建store,js文件
*注意:reducer中只能执行同步任务
import {createStore} from 'redux'
//状态对象
const initState={
count:0
}
//reducer函数更新数据状态
const reducer=(state=initState,actions)=>{
switch (actions.type) {
case 'minus':
return {...state,count:state.count+1}
case 'plus':
return{...state,count:state.count-1}
default:
return {...state}
}
}
export const store=createStore(reducer)
(2)组件内引入暴露的store
*获取值:store.getState().count
*改变值:store.dispatch({type:'minus'} //dispatch中的对象是action对象
*注意:需要使用store.subscribe()去监听state内值的变化,然后使用useState()去更改值,从而刷新界面,与useEffect(()=>{ },[ ])配合使用,只启动一次监听器,就会永久监听
import React from 'react'
import { store } from '../store/store'
import {useState,useEffect} from 'react'
export default function Nba() {
const [count,setCount] =useState(store.getState().count)
useEffect(()=>{
store.subscribe(()=>{
let count=store.getState().count
setCount(count)
})
},[])
return (
<div>
<p>{count}</p>
<button type="button" onClick={()=>store.dispatch({type:'minus'})}>加一</button>
<button type="button" onClick={()=>store.dispatch({type:'plus'})}>减一</button>
</div>
)
}
5、combineReducers(合并Reducers、适用于store.js里面有多个reducers时)
(1)使用:const reducer = combineReducers({counterReducer,cartReducer})
(2)组件中取值:let list = store.getState().cartReducer.list
6、vueX与reduX区别
(1)vuex中直接操作state状态数据是在mutations选项中,mutation中每个操作定义成一个方法;redux中直接操作state状态数据是在reducer中,只有一个函数,不同操作通过action动作的type区分
(2)reducer函数中操作state,先拷贝一份,更改之后返回一个新state
十五、Redux Toolkit库(可以直接更改state内的值,不用拷贝一份再修改)
1、安装
npm i @reduxjs/tookit
2、使用
(1)store文件夹下定义xx.js文件内,使用导入的createSlice创建对象(state的名字必须是: initialState),内含接收参数的写法,获取组件传入的参数:action.payload
import { createSlice } from '@reduxjs/toolkit'
export const cartSlice = createSlice({
name:'cart',
initialState:{
list:[{id:1001,name:'javascript',price:88.8}]
},
reducers:{
addCart(state,action){
let product = action.payload
state.list.push(product)
},
deleteCart(state,action){
let id = action.payload
let index = state.list.findIndex(item=>item.id===id)
state.list.splice(index,1)
}
}
})
export const {addCart, deleteCart} = cartSlice.actions
export default cartSlice.reducer
(2)store文件夹下定义的index.js文件内:集成多个reducers
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from './counterSlice'
import cartReduer from './cartSlice'
export default configureStore({
reducer:{
counter:counterReducer,
cart:cartReduer
}
})
(3)组件内使用
*注意:需要使用store.subscribe()去监听state内值的变化,然后使用useState()去更改值,从而刷新界面
//引入暴露的actions
import {addCart, deleteCart} from '../store/cartSlice'
//修改值
store.dispatch(addCart(product))
//获取值
store.getStore().counter.count
十六、React-Redux库(使用hooks函数简化组件内取值和改变值的操作)
1、安装
npm i react-redux
2、在index.js文件中导入Provider包裹App组件,还要导入暴露的store(让useSelector()与store建立联系,Provider透传store)
// 关联store
import { Provider } from 'react-redux'
import store from './store'
<Provider store={store}>
<App />
</Provider>
3、组件内导入useSelector,useDispatch
(1)注意:useSelector()会监听数据变化从而刷新界面,不用useState()更改值在刷新界面
(2)import { useSelector,useDispatch } from 'react-redux'
4、组件内使用
(1)获取值
const cartList= useSelector(state=>state.cart.list)
(2)修改值
const dispatch = useDispatch()
dispatch(addCart(product))
import {addCart, deleteCart} from '../store/cartSlice'
import { useSelector,useDispatch } from 'react-redux'
//获取值
const cartList= useSelector(state=>state.cart.list)
//修改值(含传参)
let product = {id:1002,name:'css编程',price:58.78}
const dispatch = useDispatch()
dispatch(addCart(product))
十七、Redux中进行异步任务操作(必须是在安装了Redux Toolkit库才行)
1、store文件夹下的xxx.js文件内
(1)引入createAsyncThunk(),创建对象,并暴露出去,在组件中使用(系统会自动封装成promise,返回三种状态:成功、加载、失败)
(2) extraReducers(builder){ }中获取异步任务返回的值,进行保存
import { createSlice,createAsyncThunk } from '@reduxjs/toolkit'
export const fetchBannerList = createAsyncThunk('banner/fetchList',async ()=>{
// 调用banner接口获取bannerList
let res = await axios({method:'get',url:'https://api.yuguoxy.com/api/shop/banner'})
let {resultCode,resultInfo} = res.data
if(resultCode === 1){
let list = resultInfo.list
return list
}
return []
})
export const bannerSlice = createSlice({
name: 'banner',
initialState: {
bannerList: [],
},
reducers: {
init(state) {}
},
extraReducers(builder){
//成功处理fulfilled
builder.addCase(fetchBannerList.fulfilled,(state,action)=>{
//返回的数据 action.payload
state.bannerList = action.payload
})
}
})
export default bannerSlice.reducer
2、组件中发起异步请求
(1)引入createAsyncThunk()所创建的暴露了的对象
(2)使用useDispatch进行传参(传入异步任务所返回的值)
import React from 'react'
import { useEffect } from 'react'
import { useSelector,useDispatch} from 'react-redux'
import {fetchBannerList} from '../store/bannerSlice'
export default function Banner() {
const bannerList = useSelector(state=>state.banner.bannerList)
const dispatch = useDispatch()
useEffect(()=>{
dispatch(fetchBannerList())
},[])
return (
<div>
<h2>banner列表</h2>
{bannerList.map(item=>{
return (
<img src={item.url} alt={item.id} key={item.id} />
)
})}
</div>
)
}