React
react生命周期函数
创建react项目
npx create-react-app 项目名(不能有大写字母)
父子组件之间传值
参考文档:https://www.cnblogs.com/chujunqiao/p/11795422.html
React 中ref的几种用法
1.字符串
通过 this.refs.a 来引用真实dom的节点
dom 节点上使用
<input type ="text" ref="a"/>
2.回调函数
回调函数就是在dom节点或组件上挂载函数,函数的入参是dom节点或组件实例,达到的效果与字符串形式是一样的,
都是获取其引用。
<input type="text" ref={(input)=>{this.textInput=input}}
3.React.createRef()
在React 16.3版本后,使用此方法来创建ref。将其赋值给一个变量,通过ref挂载在dom节点或组件上,该ref的current属性将能拿到dom节点或组件的实例
class Counter extends Component {
constructor() {
super()
this.state ={sum:0,number:0}
this.myRef = React.createRef();
}
change = () =>{
this.setState({...this.state,sum: Number(this.textInput.value) + Number(this.myRef.current.value)})
}
componentDidMount(){
console.log(this.myRef.current.value);
}
render() {
return (
<div onChange ={this.change} >
<input type="text" ref={(input)=>{this.textInput=input}} />+
<input type ="text" ref={this.myRef} /> = {this.state.sum}
</div>
)
}
}
案例:
通过ref获取form实例对象,也可以是值或者方法
子组件
import React, { Component } from 'react'
import { Form, Input, Button, Checkbox } from 'antd';
export default class Children extends Component {
render() {
const layout = {
labelCol: { span: 8 },
wrapperCol: { span: 16 },
};
const tailLayout = {
wrapperCol: { offset: 8, span: 16 },
};
const onFinish = values => {
console.log('Success:', values);
};
const onFinishFailed = errorInfo => {
console.log('Failed:', errorInfo);
};
return (
<Form ref={e => { this.addForm = e }}
{...layout}
name="basic"
initialValues={{ remember: true }}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
>
<Form.Item
label="Username"
name="username"
rules={[{ required: true, message: 'Please input your username!' }]}
>
<Input />
</Form.Item>
<Form.Item
label="Password"
name="password"
rules={[{ required: true, message: 'Please input your password!' }]}
>
<Input.Password />
</Form.Item>
<Form.Item {...tailLayout} name="remember" valuePropName="checked">
<Checkbox>Remember me</Checkbox>
</Form.Item>
<Form.Item {...tailLayout}>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
)
}
}
父组件
import React, { Component } from 'react'
import { Button } from 'antd'
import Children from './children'
export default class Parent extends Component {
/**
* 需要在子组件中form表单中加入ref={e=>{this.addForm = e}}来获取form对象
*/
getComponent = () => {
console.log(this.textInput.addForm)
}
render() {
return (
<div>
<Button type="success" onClick={() => { this.getComponent() }}>获取子组件的form对象</Button>
<Children ref={e => { this.textInput = e }} />
</div>
)
}
}
Props
用于组件之间数据的传递
export default class Home extends React.Component {
//渲染函数
render() {
const nav1 = ['web','app']
const nav2 = ['java','react']
return (
<div>
<App nav={ nav1 } title="文章列表"/>
<App nav={ nav2 } title="文章详情"/>
</div>)
}
}
通过props获取nav和title
export default class App extends React.Component {
render() {
return (
<div>
{/* 获取title*/}
<h1>{this.props.title}</h1>
{/*jsx语法,props 获取nav*/}
<ul>
{
this.props.nav.map((element,index)=>{
return <li key={index}>{element}</li>
})
}
</ul>
</div>
)
}
}
state
修改元素的变化使用state
export default class StateComponent extends React.Component{
// 初始化state
constructor(){
super()
this.state = {
count:1,
flag: true
}
}
increment(){
//通过setState方法来修改state中count的值
this.setState({
count: this.state.count+1
})
}
render(){
let showView = this.state.flag ? '已登录' : '未登录'
return (
<div>
{this.state.count}
<br/>
<button onClick={this.increment.bind(this)}>增加</button>
<h1>{showView}</h1>
</div>
)
}
}
setState同步异步问题
参考文档:https://blog.csdn.net/sinat_17775997/article/details/77100815
Fetch的使用
处理get和post请求
官网:https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch
案例:https://blog.csdn.net/qq_34475058/article/details/84238108
react跨域问题
参考文档:https://www.jianshu.com/p/4a7f26adbedf
setupProxy.js
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app){
app.use('/api', createProxyMiddleware(
{
target: 'http://47.96.0.211:9000',
changeOrigin: true,
}
))
}
react 路由
英文文档:https://reactrouter.com/web/guides/quick-start
中文文档:https://www.jianshu.com/p/97e4af32811a
安装:npm install react-router-dom
案例:使用了link和
两种方式
import React from "react"; import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom"; export default function App() { return ( <Router> <div> <nav> <ul> <li> <Link to="/">Home</Link> </li> <li> <Link to="/about">About</Link> </li> <li> <Link to="/users">Users</Link> </li> </ul> </nav> {/* A <Switch> looks through its children <Route>s and renders the first one that matches the current URL. */} <Switch> <Route path="/about"> <About /> </Route> <Route path="/users"> <Users /> </Route> <Route path="/"> <Home /> </Route> </Switch> </div> </Router> ); } function Home() { return <h2>Home</h2>; } function About() { return <h2>About</h2>; } function Users() { return <h2>Users</h2>; }
Antd
为了提高性能:因为在加载antd组件时会将css、js全部加载进来,性能会降低所以要使用按需加载
使用第一种方法 babel-plugin-import(需要注意不能先上传git中,先把项目中的隐藏文件.git文件夹删除)
按需加载:https://blog.csdn.net/qq_40273756/article/details/103409009和https://segmentfault.com/a/1190000019781597【这个方式还需要修改package.json的script如下】
"scripts":{ "start":"react-app-rewired start", "build":"react-app-rewired build", "test":"react-app-rewired test", "eject":"react-scripts eject" }
**自定义主题和react集成less文件[类似css文件,可以使用嵌套样式]文档:**https://blog.csdn.net/weixin_44606225/article/details/107499892
Redux
官网:http://cn.redux.js.org/
参考资料:http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_one_basic_usages.html
redux和react-redux:https://blog.csdn.net/qq_42767631/article/details/83096841和https://segmentfault.com/a/1190000015396885
redux获取store中的数据:https://www.cnblogs.com/luguankun/p/11229020.html
**定义:**Redux是 JavaScript 状态容器,提供可预测化的state状态管理
Redux三大原则
1、唯一数据源
整个应用的state都被存储到一个状态树里面,并且这个状态树,只存在于唯一的store中
2、保持只读状态
state是只读的,唯一改变state的方法就是触发action,action是一个用于描述以发生时间的普通对象
3、数据改变只能通过纯函数执行
使用纯函数来执行修改,为了描述action如何改变state的,你需要编写reducers
store、state和action之间的关系
stroe 保存数据
action领导 下达命令
reducer员工 执行命令
(一)store
store就是保存数据的地方,你可以把它看成一个数据,整个应用智能有一个store
Redux提供createStore这个函数,用来生成Store
提供方法: getState(), dispatch(action), subscribe(listener)
import {createStore} from 'redux'
const store=createStore(fn);
(二) State
state就是store里面存储的数据,store里面可以拥有多个state,Redux规定一个state对应一个View,只要state相同,view就是一样的,反过来也是一样的,可以通过**store.getState( )**获取
import {createStore} from 'redux'
const store=createStore(fn);
const state=store.getState()
(三)Action
state的改变会导致View的变化,但是在redux中不能直接操作state也就是说不能使用this.setState来操作,用户只能接触到View。在Redux中提供了一个对象来告诉Store需要改变state。Action是一个对象其中type属性是必须的,表示Action的名称,携带的数据是字符串‘redux原理’,Action描述当前发生的事情,这是改变state的唯一的方式
默认是对象(同步action), {type: ‘xxx’, data: value}, 需要通过对应的actionCreator产生,
它的值也可以是函数(异步action), 需要引入redux-thunk才可以
const action={
type:'ADD_TODO',
payload:'redux原理'
}
(四)dispatch( )
dispatch(action )是view发出Action的唯一办法,接收一个Action作为参数,将它发送给store通知store来改变state,触发reducer调用
import { createStore } from 'redux';
const store = createStore(fn);
dispatch({
type: 'ADD_TODO',
payload: 'Learn Redux'
});
(五) Reducer
Store收到Action以后,必须给出一个新的state,这样view才会发生变化。这种state的计算过程就叫做Reducer。根据老的state和指定的action, 返回一个新的state
不能修改老的state
const reducer =(state,action)=>{
switch(action.type){
case ADD_TODO:
return newstate;
default return state
}
}
(六)bindActionCreators
作用:bindActionCreators的作用是将一个或多个action和dispatch组合起来生成mapDispatchToProps需要生成的内容。
参考文档:https://www.cnblogs.com/mengff/p/9530286.html
(七)combineReducers
作用:把一个由多个不同 reducer 函数作为 value 的 object,合并成一个最终的 reducer 函数,然后就可以对这个 reducer 调用 createStore。
参考文档:https://www.jianshu.com/p/6a041ad8abdb
(八)中间件redux-thunk
作用:处理异步的中间件
参考文档:https://www.jianshu.com/p/51c8eaa9fa2a
**案例:**使用redux
app.jsx
import React from 'react';
import { Button } from 'antd';
export default class App extends React.Component {
render() {
return (
<>
<h1>{this.props.value}</h1>
<Button onClick={this.props.onIncrement} type="primary" block >
increment
</Button>
<Button onClick={this.props.onDecrement} block>
decrement
</Button>
</>
)
}
}
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App'
import { createStore } from 'redux'
import reducer from './reducers/counter'
//redux创建一个仓库
const store = createStore(reducer)
//添加一个render方法,使页面重新渲染,否则页面上的值不会改变
const render = ()=>{
ReactDOM.render(
<App
onIncrement = {()=>{store.dispatch({type:"INCREMENT"})}}
onDecrement = {()=>{store.dispatch({type:"DECREMENT"})}}
value = {store.getState()}
/>,
document.getElementById('root')
);
}
render()
store.subscribe(render)
reducer中的counter.js
const counter = (state = 0,action) =>{
switch(action.type){
case "INCREMENT":
return state+1;
case "DECREMENT":
return state-1;
default:
return state;
}
}
export default counter
React-redux
需要引用redux,react-redux,redux-thunk,redux-devtools-extension(在chrome浏览器中使用redux工具)
定义:React-Redux是Redux的官方React绑定库。它能够使你的React组件从Redux store中读取数据,并且向store分发actions以更新数据
案例:react-redux流程:https://blog.csdn.net/m0_37282983/article/details/77982564
Provider
connect
方法生成容器组件以后,需要让容器组件拿到state
对象,才能生成 UI 组件的参数。一种解决方法是将state
对象作为参数,传入容器组件。但是,这样做比较麻烦,尤其是容器组件可能在很深的层级,一级级将state
传下去就很麻烦。React-Redux 提供Provider
组件,可以让容器组件拿到state
下面代码:在最外层容器中,把所有内容包裹在 Provider 组件中,将之前创建的 store 作为 prop 传给 Provider ,App的所有子组件就默认都可以拿到state
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import todoApp from './reducers'
import App from './components/App'
let store = createStore(todoApp);
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
connect
作用是将UI 组件生成容器组件
UI组件:如
const Title = value => <h1>{value}</h1>;
- 只负责 UI 的呈现,不带有任何业务逻辑
- 没有状态(即不使用
this.state
这个变量)- 所有数据都由参数(
this.props
)提供- 不使用任何 Redux 的 API
容器组件:
- 负责管理数据和业务逻辑,不负责 UI 的呈现
- 带有内部状态
- 使用 Redux 的 API
import { connect } from 'react-redux'
const VisibleTodoList = connect(
mapStateToProps, //是否需要一般属性,如果是就写这个,否则不需要
mapDispatchToProps //是否需要函数属性,如果是就写这个,否则不需要
)(TodoList)
//统一格式:
connect(
state=>({key:value}),
{action中的方法} //如不需要放一个{}即可
)(ui组件)
//例子:
connect(
state => ({user:state.user}),
{setHeadTitle}
)(withRouter(LeftNav))
mapStateToProps()
参考文档:https://www.cnblogs.com/orzzt/p/9831343.html
负责输入逻辑,即将state映射到 UI 组件的参数(props),mapStateToProps执行后应该返回一个对象,Redux中的数据映射到React中的props中去
getVisibleTodos也是一个函数,可以从state算出 todos 的值
const mapStateToProps = (state) => {
return {
todos: getVisibleTodos(state.todos, state.visibilityFilter)
}
}
mapDispatchToProps()
负责输出逻辑,将用户对 UI 组件的操作映射成 Action。它定义了哪些用户的操作应该当作 Action,传给 Store,使用时this.props.onClick即可
const mapDispatchToProps = (dispatch,ownProps) => {
return {
onClick: () => {
dispatch({
type: 'SET_VISIBILITY_FILTER',
filter: ownProps.filter
});
}
};
}
案例:计数器
import React, { Component } from 'react'
import PropTypes from 'prop-types' //类型检查
import ReactDOM from 'react-dom'
import { createStore } from 'redux'
import { Provider, connect } from 'react-redux'
// 定义counter组件
class Counter extends Component {
render() {
const { value, onIncreaseClick } = this.props
// const value = this.props.value
return (
<div>
<span>{value}</span>
<button onClick={onIncreaseClick}> +1</button>
</div>
)
}
}
//对Counter组件接受的props进行类型检查
Counter.propTypes = {
value: PropTypes.number.isRequired, //要求数字类型,没有提供会警告
onIncreaseClick: PropTypes.func.isRequired //要求函数类型
}
// Action
const increaseAction = { type: 'increase' }
// Reducer 基于原有state根据action得到新的state
function counter(state = { count: 0 }, action) {
const count = state.count
switch (action.type) {
case 'increase':
return { count: count + 1 }
default:
return state
}
}
// 根据reducer函数通过createStore()创建store
const store = createStore(counter)
// 将state映射到Counter组件的props
function mapStateToProps(state) {
return {
value: state.count
}
}
// 将action映射到Counter组件的props
function mapDispatchToProps(dispatch) {
return {
onIncreaseClick: () => dispatch(increaseAction)
}
}
// 传入上面两个函数参数,将Counter组件变为App组件
const App = connect(
mapStateToProps,
mapDispatchToProps
)(Counter)
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
React使用G2图表数据可视化
在React项目中使用bizCharts,它的底层封装了g2
参考文档:https://bizcharts.net/product/bizcharts/gallery#LineChart
注意:**复制代码时注意版本号问题需要将@3.5.8与你安装的版本对应或者删除它:**在官网上bizCharts 3版本的图表代码中有这一行代码:
import { Chart, Axis, Tooltip, Geom, Legend, View } from ‘bizcharts@3.5.8’;
Immutable.js
参考文档:https://www.jianshu.com/p/0fa8c7456c15
React项目各个模块的思路
(1)商品管理中的添加和修改
1.使用的是同一个表单
2.通过点击添加/修改按钮时,当修改时会传入一个product对象,需要在render渲染之前,来判断是否传入了product,通过this.props.localtion.state获取传过来的product,在this中存入isUpdate,以便其他方法可以获取到(!!是将表达式强制转化为bool值的运算,运算结果为true或false,表达式是什么值,结果就是对应的bool值,不再取非不是取非再取非的意思),顺便把product存入this中
componentWillMount() {
//点击修改时会传入一个product
const product = this.props.location.state
this.isUpdate = !!product
//在this中保存商品信息
this.product = product || {}
}
3.在修改中需要填充表单,需要在表单<Form.Item>中添加属性 initialValue={product.name},(product.name指该输入框或文本域中对应的值)
(2)上传图片
import { Upload, Modal, message } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import React from 'react'
import { reqDeletImg } from '../../api/index'
import PropType from 'prop-types'
class PicturesWall extends React.Component {
static propType = {
imgs: PropType.array
}
/* state = {
previewVisible: false,
previewImage: '',
previewTitle: '',
fileList: [
{
uid: '-1',
name: 'image.png',
status: 'done',
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
}
],
}; */
constructor(props) {
super(props)
//填充表单时需要初始化fileList的值
let fileList = []
const { imgs } = this.props
if (imgs && imgs.length > 0) {
fileList = imgs.map((img, index) => (
{
uid: -index,
name: img,
status: 'done',
url: 'http://localhost:5000/upload/' + img,
}))
}
this.state = {
previewImage:'',
previewVisible: false,
fileList
}
}
/**
* 获取图片
*/
getImgs = () => {
//console.log('getImgs'+this.state.fileList)
return this.state.fileList.map(file => file.name)
}
handleCancel = () => this.setState({ previewVisible: false });
handlePreview = async file => {
//console.log('handlePreview()'+JSON.stringify(file))
//显示指定file的大图
this.setState({
previewImage: file.url || file.thumbUrl,
previewVisible: true,
});
};
handleChange = async ({ file, fileList }) => {
//修正file的信息name、url
if (file.status === 'done') {
const result = file.response
//console.log('file.response'+JSON.stringify(result))
if (result.status === 0) {
message.success('上传成功')
const { name, url } = result.data
//file赋值为最后上传的那张图片
file = fileList[fileList.length - 1]
file.name = name
file.url = url
} else {
message.error('上传图片失败')
}
} else if (file.status === 'removed') { //删除图片
const result = await reqDeletImg(file.name)
if (result.status === 0) {
message.success('删除成功')
} else {
message.error('删除失败')
}
}
this.setState({ fileList })
};
render() {
const { previewVisible, previewImage, fileList, previewTitle } = this.state;
const uploadButton = (
<div>
<PlusOutlined />
<div className="ant-upload-text">Upload</div>
</div>
);
return (
<div className="clearfix">
<Upload
action="/manage/img/upload"
listType="picture-card"
name="image"
fileList={fileList}
onPreview={this.handlePreview}
onChange={this.handleChange}
>
{fileList.length >= 8 ? null : uploadButton}
</Upload>
<Modal
visible={previewVisible}
title={previewTitle}
footer={null}
onCancel={this.handleCancel}
>
<img alt="example" style={{ width: '100%' }} src={previewImage} />
</Modal>
</div>
);
}
}
export default PicturesWall