1. react请求
1. react发送ajax请求
发送请求需要在挂载完成或者数据更新之后
import React, {Component} from 'react'
import axios from 'axios'
class App extends Component {
constructor () {
super()
this.state = {
count: 0,
isShow: true,
msg: ''
}
console.log('constructor执行了');
}
render () {
console.log('render执行了');
return (
<div>
当前数据:{this.state.msg}
</div>
)
}
async componentDidMount () {
const data = await axios({
url: 'http://localhost:3000/content'
}).then(data => data.data)
console.log(data)
this.setState(data[0])
console.log('挂在完成')
}
shouldComponentUpdate () {
console.log('更新');
return true
}
componentDidUpdate () {
console.log('更新已完成 ');
}
}
export default App;
2. mock数据
在静态public文件中创建相关的文件夹书写数据
import React, {Component} from 'react'
import axios from 'axios'
class App extends Component {
constructor () {
super()
this.state = {
msg: []
}
console.log('constructor执行了');
}
async handler () {
const data = await axios({
url: '/mock/db.json'
}).then(data => data.data)
console.log(data);
this.setState({
msg: data
})
}
render () {
console.log('render执行了');
return (
<div>
<button onClick={() => {this.handler()}}>点击</button>
当前数据:{
<ul>
{
this.state.msg.map(item => {
return (<li key={item.id}>{item.name}</li>)})
}
</ul>
}
</div>
)
}
}
export default App;
3. react请求转发
- 请求转发
由于存在跨域的问题,不同源 服务端对服务端不存在跨域问题
所以如果请求服务a没有的数据跨域被服务器a转发给服务器b - 实现跨域请求的方法
- 方法一:package.json代理
在package.json中书写代理proxy属性,值为代理的端口,这样书写的请求路径只需要后面的路径,当请求发现没有前面的端口就会到package.json中寻找配置端口
缺点是只能存储一个地址 - 方法二:第三方包代理,http-proxy-middleware
导入包中的createProxyMiddleware在setupProxy.js文件书写
- 方法一:package.json代理
// 方法一
// packkage.json
"proxy": "http://localhost:3005"
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const app = express();
// 路径对应代理的地址,然后开启同源跨域即可
// 第一个参数会和target进行拼接,请求的url需要比第一参数的路径要多或者相等
app.use('/api', createProxyMiddleware({ target: 'http://www.example.org', changeOrigin: true }));
app.listen(3000);
2. redux
2.1 redux概念
- 随着组建的增多,数据之间的数据交互会变的日益复杂,所以引入了redux进行数据的管理
- store是数据存储的位置,reducers是数据操作的函数
2.2 创建store及reducer
- store不属于任何组件,所以需要一个独立的文件进行存储
- 安装redux,通过内部的createStore进行创建,接收的参数就是操作的数据
- 通过createStore.getState()获取导入的数据
- reduser是储存数据的文件
// 导入redux中的createStore方法
// index.js
import { createStore } from 'redux'
import handler1 from './store/reduser/a.reduser'
// 创建 传入一个方法
const store = createStore(handler1)
// 获取
console.log(store.getState())
// a.reduser
const handler1 = () => {
return {
count: 0
}
}
export default handler1
2.3 获取store数据
- 安装react-redux,在index.js中引入Provider并包裹App标签,传参类似于属性传参
- 在需要引入数据的组件中引入react-redux中的connect方法,数据会存储在props中
- conect方法中传入回调函数,并且会返回一个新函数,所以需要再次调用,接收的参数要获取数据的组件
- connect中回调函数传入的参数是在Provider传入中的数据,回调函数中操作的对象就是props中的数据
- 需要配合组件一起导出,不然props不能接收到数据
index.js
import { createStore } from 'redux'
import handler1 from './store/reduser/a.reduser'
import { Provider } from 'react-redux'
const store = createStore(handler1)
console.log(store.getState());
ReactDOM.render(
<React.StrictMode>
{/* 相当于是将Provider与后面的组件建立关系 */}
<Provider store={store}><App /></Provider>
</React.StrictMode>,
document.getElementById('root')
);
Counter
// 组件
import React, {Component} from 'react'
import {connect} from 'react-redux'
function Counter (props){
return (
<div>
<button>-1</button>
<span>{props.count}</span>
<button>+1</button>
</div>
)
}
// 将index.js中导入的reducer数据传递到组件中
// Provider传递的参数就是reduser数据,也就是传入到store的参数
const storeProps = (state) => ({
count: state.count
})
export default connect(storeProps)(Counter)
2.4 改变store数据
- 通过传入组件中的dispatch()方法进行修改,在type配置中设置相关属性名
- 在store文件中导出函数
- 参数1是理解为数据占位符,保存相关的数据
- 参数2是dispatch传入的配置
reduser数据 Counter.reducer.js
// Counter.reducer.js
const titleState = {
count1: 6
}
const handler1 = (state = titleState, action) => {
console.log(action);
switch (action.type) {
case 'addone':
return {
// 属性名要与数据中的titleState中的数据属性相同
count1: ++state.count1
}
case 'removeone':
return {
count1: --state.count1
}
default:
return state
}
}
export default handler1
Counter
import React, {Component} from 'react'
import {connect} from 'react-redux'
class About extends Component {
constructor(props) {
super(props)
this.state = {}
}
render () {
return (
<>
<button onClick={() => {this.props.dispatch({type: 'removeone'})}}>-1</button>
<span>{console.log(this.props)}</span>
<span>{this.props.count}</span>
<button onClick={() => {this.props.dispatch({type: 'addone'})}}>+1</button>
</>
)
}
}
// store 参数是store.reduser中传出的数据,也就是state
const aboutStore = (store) => ({
// 属性名就是传入props中使用的属性名
count: store.count1
})
// connect意思是让组件和index.js中通过Provider导入的reduser数据进行联系
export default connect(aboutStore)(About)
2.5 action 传递参数
dispatch的配置会以对象的形式传入reducer函数的第二个参数,所以action.属性名即可调用配置的数据
Counter.reducer.js
// reducer
const titleState = {
count1: 6
}
const handler1 = (state = titleState, action) => {
console.log(action);
switch (action.type) {
case 'addone':
return {
// 属性名要与数据中的titleState中的数据属性相同
count1: ++state.count1
}
case 'removeone':
return {
count1: --state.count1
}
case 'addn':
return {
count1: state.count1 + action.payload
}
default:
return state
}
}
export default handler1
Counter
// 组件
import React, { Component } from 'react'
import { connect } from 'react-redux'
class Counter extends Component {
constructor(props) {
super(props)
this.state = {}
}
render() {
return (
<>
<button onClick={() => { props.dispatch({type: 'removeone'})}}>-1</button>
<button onClick={() => { props.dispatch({type: 'addn', payload: 5})}}>+5</button>
<span>{this.props.count}</span>
<button onClick={() => { props.dispatch({type: 'addone'})}}>+1</button>
</>
)
}
}
// store 参数是store.reduser中传出的数据,也就是state
const aboutStore = (store) => ({
// 属性名就是传入props中使用的属性名
count: store.count1
})
export default connect(aboutStore)(Counter)
2.6 提取action代码为函数
优化书写的reduser代码
- 调用dispatch,并重新包裹dispatch
- connect()两个回调函数
第一个回调函数的参数是index.js中传入Provider的数据,也就是reduser中的数据,用于更新state,是输入源
第二个回调函数的参数,用于action,输出源,也就是执行的方法
// 组件
import React, { Component } from 'react'
import { connect } from 'react-redux'
class About extends Component {
constructor(props) {
super(props)
this.state = {}
}
render() {
return (
<>
<button onClick={() => { this.props.removeone()}}>-1</button>
<button onClick={() => { this.props.addn(5)}}>+5</button>
<span>{console.log(this.props)}</span>
<span>{this.props.count}</span>
<button onClick={() => { this.props.addone() }}>+1</button>
</>
)
}
}
// store 参数是store.reduser中传出的数据,也就是state
const aboutStore = (store) => ({
// 属性名就是传入props中使用的属性名
count: store.count1
})
const mapState = dispatch => ({
addone(){
dispatch({ type: 'addone' })
},
removeone () {
dispatch({type: 'removeone'})
},
addn (n) {
dispatch({type: 'addn', payload: n})
}
})
export default connect(aboutStore, mapState)(About)
2.7 自动生成action触发函数
- 需要借助redux中的bindActionCreators方法
第一个参数是对象,存储的方法名称
第二个参数是dispatch - bindActionCreators会返回一个新对象,但是connect的第二个参数返回的是一个对象,所以需要解构bindActionCreators再放入connect第二个参数
- 第一个参数对象内保存的是函数形式的方法
// 基本用法
bindActionCreators({
addone() {
return { type: 'addone'}
}
}, dispatch )
// 组件
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import * as j from '../store/actions/action'
console.log(j);
class About extends Component {
constructor(props) {
super(props)
this.state = {}
}
render() {
return (
<>
<button onClick={() => { this.props.removeone()}}>-1</button>
<button onClick={() => { this.props.addn(5)}}>+5</button>
<span>{console.log(this.props)}</span>
<span>{this.props.count}</span>
<button onClick={() => { this.props.addone() }}>+1</button>
</>
)
}
}
// store 参数是store.reduser中传出的数据,也就是state
const aboutStore = (store) => ({
// 属性名就是传入props中使用的属性名
count: store.count
})
const mapState = dispatch => ({
...bindActionCreators(j, dispatch)
})
export default connect(aboutStore, mapState)(About)
actions.js
// actions
export const addone = () => ({type: 'addone'})
export const removeone = () => ({type: 'removeone'})
export const addn = (payload) => ({type: 'addn', payload: payload})
2.8 设置action类型常量
多次引用,方便使用,优化复制
声明
export const ADDONE = 'addone'
export const ADDN = 'addn'
export const REMOVEONE = 'removeone'
使用
import { ADDONE, REMOVEONE } form '.action_type'
2.9 redux拆分与合并
- 在多组件中,将多种将变量,方法,数据分别存放在不同的文件中,通过导入的操作导入到相关的文件,然后再传入组件中
- redux中导入combineReducers()方法,参数是配置对象
- 存放数据的文件中保存着数据和reducer相关的方法
- 组件中存放相关的方法,并用bindActionCreators进行存储
1. 数据存储操作的js
/reducer/person.reducer.js
// 数据存储操作的js
import { ADDPERSON } from '../action_type/person.type'
const personStore = [
{
id: 1,
name: 'zs'
}
]
const person = (state = personStore, action) => {
switch (action.type) {
case ADDPERSON:
return [
...state,
action.payload
]
default:
return state
}
}
export default person
2. reducer集合
/reducer/index.js
import {combineReducers} form 'redux'
import personReducer form './Person.reducer'
import counterReducer form './Counter.reducer'
export default combineReducers({
counter: counterReducer,
person: personReducer
})
3. 根目录index.js
index.js
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
// 引入创建store数据的方法
import {createStore} from 'redux'
// 引入数据(上一个文件)
import totalStore from './store/reducers/index.js'
// 数据建立联系
import {Provider} from 'react-redux'
const store = createStore(totalStore)
ReactDOM.render(
<React.StrictMode>
<Provider store={store}><App /></Provider>
</React.StrictMode>,
document.getElementById('root')
);
4. 组件
/components/Person.js
// 生成action触发函数的方法,参数1是对象,参数2是相关的函数
import React from 'react';
import { bindActionCreators } from 'redux'
// 用于让store和组件产生联系
import { connect } from 'react-redux'
import * as personActions from '../store/actions/person.actions'
function Person(props) {
return (
<div>
<button onClick={() => { props.addPerson({ id: props.personReducer.length + 1, name: 'lis' }) }}>点击添加</button>
<ul>
{props.personReducer.map(item => {
return (
<li key={item.id}>{item.name}</li>
)
})}
</ul>
</div>
)
}
const personStore = state => ({
personReducer: state.person
})
const personAction = dispatch => ({
...bindActionCreators(personActions, dispatch)
})
export default connect(personStore, personAction)(Person)
5. 存放方法的js
/action/person.action.js
// 存放方法的js
import { ADDPERSON } from './action.type'
export const addPerson = (payload) => ({ type: ADDPERSON, payload })
6. 存放变量类型的js
/action_type/person.action.typse.js
// 存放变量类型的js
export const ADDPERSON = 'addPerson'
2.7 redux 工作流程梳理
第一大步
- 创建数据store并保存,关联reducer
- 在index.js中引入redux的createStore方法,引入需要store管理的数据
- 通过createStore存储数据
import { createStore } form 'redux'
const store = createStore()
- 创建reducer,参数含义
- state:store接收到相关指令之后,就会将state传递给相应的reducer,reducer通过处理会返回一个结果,简单的说,state就是返回给store进行存储的数据
- action:store传递给reducer的具体指令,action.payload存储传入的数据
- reducer通过判断action.type进行数据的分支处理
function reducer (state = initState, action) {
switch(action.type){
case 'add':
return {
content : [
...state.content
action.content
]
}
}
}
- 给state定义初始数据
const initState = {
content: ['默认数据']
}
第二大步
- 第二大步,store向组件进行传递
- 导入react-redux中的Provider标签,并通过store属性传递store
import {Provider} from 'react-redux'
const store = createStore(totalStore)
ReactDOM.render(
<React.StrictMode>
<Provider store={store}><App /></Provider>
</React.StrictMode>,
document.getElementById('root')
);
- 需要用到store数据的子组件中,通过react-redux中的connect进行关联
- connect调用后返回的是一个函数,返回函数的参数是想要关联的组件
import { connect } from 'react-redux'
export default connect()(App)
- 在具体的组件中使用connect方法获取store里保存的数据,通过组件的props进行访问
- connect内部参数是两个函数方法,最终都可以通过props进行接收
- 参数1,是store数据,目的是操作数据,获取组件需要的数据
- 参数2,是dispatch重新包裹的方法
- dispatch => (函数名()=> dispatch({配置项})),最原始的方法
- dispatch可以通过bindActionCreators进行函数触发事件的绑定,优化分离的方法
- bindActionCreator
- 第一个参数是函数方法的配置型,也就是多个原始方法
- 第二个参数是dispatch
- 获取数据之后可以在界面上渲染
import { connect } from 'react-redux'
class App extends Component {
render() {
return (
<>
<input type="text" ref={this.myRef}
<button onClick={this.handler}>add</button>
<ul>
{
this.props.conent.map((item, index) => <li key={index}>内容{item}</li>)
}
</ul>
</>
)
}
}
// 从store当众获取当前组件需要使用的数据
const mapStateToProps = (state) => ({
content: state.content
})
export default connect(mapStateToProps)(App)
- 数据更改后自动更新界面
- 输入框中的内容需要先获取元素
- 通过React.createRef()创建对象
- 让输入框的ref属性绑定给React.createRef()就可以获取
- 通过React.createRef().current获取绑定了React.createRef()元素的DOM节点
constructor () {
super()
this.myRef = React.createRef()
}
handler = () => {
const content = this.myRef.current.value
this.props.dispatch({type: 'add', content})
this.myRef.current.value = ''
}
简单版
- 创建store保存相关数据关联reducer
- 利用provider将store向后传递
- 在具体的组件当中使用connect方法获取store里面保存的数据
- 当我们拿到数据之后就可以在界面上渲染了
2.8 redux使用代码优化
--src
|--store
| |--Reduces
| | |--Content.reducer.js
| | |--index.js
| |--Actions
| | |--Content.action.js
| |--Action_types
| |--Content.action.types.js
|
|--App.js
|--index.js
Content.reducer.js
const initState = {
content: ['默认数据']
}
function reducer (state = initState, action) {
switch(action.type){
case 'add':
return {
content : [
...state.content
action.content
]
}
}
}
export default reducer
/reducer/index.js
import {combineReducers} form 'redux'
import contentReducer form './Content.reducer'
export default combineReducers({
contentReducer: contentReducer
})
index.js
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
// 引入创建store数据的方法
import {createStore} from 'redux'
// 引入数据(上一个文件)
import totalStore from './store/reducers/index.js'
// 数据建立联系
import {Provider} from 'react-redux'
const store = createStore(totalStore)
ReactDOM.render(
<React.StrictMode>
<Provider store={store}><App /></Provider>
</React.StrictMode>,
document.getElementById('root')
);
/action/Content.action.js
// 存放方法的js
import { ADD } from './action.type'
export const add = (content) => ({ type: ADD, content })
App.js
import { connect } from 'react-redux'
class App extends Component {
render() {
return (
<>
<input type="text" ref={this.myRef}
<button onClick={this.handler}>add</button>
<ul>
{
this.props.conent.map((item, index) => <li key={index}>内容{item}</li>)
}
</ul>
</>
)
}
}
// 从store当众获取当前组件需要使用的数据
const mapStateToProps = (state) => ({
content: state.content
})
const mapDispatchToProps = (dispatch) => {
...bindActionCreators(contentActions, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps)(App)
2.9 redux中间件工作流程
- 中间件的引入是为了让异步执行的操作先做完
- 中间件的本质也是函数,他会在组件发送指令时触发,也就是action操作时触发
- 需要从redux中导入applyMiddleware方法,并作为createStore的第二个参数(第一个参数是reducer),内部存储的是函数方法,内部的函数方法需要返回一个函数内部,该函数内部书写异步任务,异步的操作依旧是需要返回一个函数,此函数就能触发生效
- store.dispatch({配置体})是直接执行,包裹的函数调用了才会触发
- 在异步执行完成后,还需要将action给store继续往后传递
// index.js
import {createStore, applyMiddleware} from 'redux'
function reducer(state, action) {
return state
}
// middle 参数1,是store的数据
// 参数2是操作action操作时,操作转交,目的是为了提前进行action的相关操作,再结合后面的action参数
function middle(getState, dispatch) {
// 参数是向后传递的方法,要传递到redux的数据通过next方法包裹
return function (next) {
return function (action) {
// 参数保存的是action的配置
// 需要操作数据向后面传递时,那么就可以通过属性交给action
action.payload = 100
next(action)
}
}
}
const store = createStore(reducer, applyMiddleware(middle))
store.dispatch({type: 'test'})
2.10 redux异步解决方案
2.10.1 redux-thunk异步解决方案
- 安装redux-thunk,在applyMiddleware()中进行引入
- 在原本存放dispatch方法的文件中就可以返回一个函数,函数的参数是dispatch,可以直接触发后续的操作
// person.action.js
import axios from 'axios'
export const addPerson = () => async (dispatch) => {
let person = await axios.get("地址").then(res => res.data)
// 直接传递给reducer中的action
dispatch({type: 'loadPersonSuccess', payload: person})
}
// index.js
import {createStore, applyMiddleware } form 'redux'
improt thunk from 'redux-thunk'
const store = createStore(reducer, applyMiddleware(thunk))
2.10.2 react-saga异步数据解决方案
- 安装redux-saga,引入默认方法createSagaMiddleware,在applyMiddleware中注册createSageMiddleware(),然后给createSageMiddleware().run
- createSageMiddleware().run方法内部参数是sage文件输出的方法需要在applyMiddleware()注册之后
- saga文件中引入takeEvery和put方法
- takeEvery负责拦截进行的操作,接收指令,参数1是拦截的dispatch指令名,参数2是执行方法
- put负责重新触发dispatch后续指令,传递给store,也就是reducer中的action
// sage.js
import axios from 'axios'
import {takeEvery, put} from 'redux-saga/effects'
function* addPerson() {
let persons = yield axios.get("地址").then(res => res.data)
// 重新触发dispatch
yield put({type: 'addPerson_success', payload: persons})
}
export default function* personSage() {
// 拦截的参数
yield takeEvery('addPerson', addPerson)
}
// app.js
import React, {Component} from 'react'
import Person from './components/Person'
import {connect} from 'react-redux'
// 生成action触发函数的方法,参数1是对象,参数2是相关的函数
import { bindActionCreators } from 'redux'
import * as personActions from './store/actions/person.actions'
class App extends Component {
render () {
return (
<div>
{console.log(this.props)}
<button onClick={() => {this.handler()}}>获取数据</button>
<Person />
</div>
)
}
handler () {
console.log(123)
this.props.addPerson()
}
}
const mapStateProps = (state) => ({
perosn: state.personReducer
})
const mapActionProps = dispatch => {
return {
...bindActionCreators(personActions, dispatch)
}
}
export default connect(mapStateProps, mapActionProps)(App);
// index.js
import createSagaMiddleware from 'redux-saga'
import personSage from './store/saga/person.saga'
// 注册为中间件
const sagaMiddleware = createSagaMiddleware()
console.log(sagaMiddleware);
const store = createStore(totalStore, applyMiddleware(sagaMiddleware))
// 让saga的方法与组件产生联系
sagaMiddleware.run(personSage)
store.dispatch({type: 'test'})
2.10.3 redux-saga拆分与合并
- 在存放方法的root.saga文件中中导入redux-saga/effects中的all方法,导出函数
- all方法参数接收的是数组,内部存放的是多有的saga方法
- 在index.js中,用redux-saga默认导出的方法.run()方法进行注册
- 内部需要的是执行的结果,使用数组内的函数需要调用
// root.saga.js
import {all} from 'redux-saga/effects'
import personSaga from './person.saga'
export default function* rootSaga() {
yield all([
personSaga()
])
}
// index.js
...
import createSagaMiddleware from 'redux-saga'
import rootSage from './store/saga/root.sage'
// 注册为中间件
const sagaMiddleware = createSagaMiddleware()
console.log(sagaMiddleware);
const store = createStore(totalStore, applyMiddleware(sagaMiddleware))
sagaMiddleware.run(rootSage)
...
2.11 简化reducer和action
- 安装redux-actions
- 在actions中引入creatAction方法,内部的参数是指令的名称,并且导出返回一个新的函数值
- 这reducer文件中引入handlerActions,并导出整体的配置
- 参数1,配置的对象的reducer方法
- 参数2,初始值
// actions.js
import {createAction} from 'redux-actions'
import {INCREMENT, DECREMENT} from '../action_type/counter.type'
// export const increment = () => ({type: INCREMENT})
// export const decrement = () => ({type: DECREMENT})
export const decrement_actions = createAction(DECREMENT)
export const increment_actions = createAction(INCREMENT)
// reducer.js
import {handleActions as createReducer} from 'redux-actions'
import {decrement_actions, increment_actions} from '../actions/counter.actions'
const titleState = {
count: 0
}
// 参数1是分开的指令,参数2是当前的初始值
export default createReducer({
[decrement_actions]: (state, action) => ({count: state.count - 1}),
[increment_actions]: (state, action) => ({count: state.count + 1})
}, titleState)
// export default function AppState(state = titleState, action) {
// switch (action.type) {
// case INCREMENT:
// return {
// count: state.count + 1
// }
// case DECREMENT:
// return {
// count: state.count - 1
// }
// default:
// return state
// }
// }
// app.js
import {connect} from 'react-redux'
import { bindActionCreators } from 'redux';
import * as counterActions from './store/actions/counter.actions'
function App(props) {
return (
<div>
<button onClick={() => {props.decrement_actions()}}>-1</button>
<span>{props.count}</span>
<button onClick={() => {props.increment_actions()}}>+1</button>
</div>
);
}
const mapAppProps = state => ({
count: state.counter.count
})
const mapAppActions = dispatch => ({
...bindActionCreators(counterActions, dispatch)
})
export default connect(mapAppProps, mapAppActions)(App)