React实战开发-----一个有关兰州疫情分析的软件,本人负责前端开发,本博客记录整个开发的流程,供大家参考
目录
- 前言
- react介绍(觉得这些官方介绍啰嗦的直接看个人总结)
-
- 一、React的起源和发展
- 二、React的出发点
- 三、Recat与传统MVC的关系
- 四、React高性能的体现:虚拟DOM
- 五、React的特点和优势
- 个人总结
- 项目初试
- 核心功能及技术
-
- 地图导入
- 开源组件的利用(时间轴)
- Ajax与postman的结合使用
-
- Ajax是什么?
- 为什么需要Ajax
- ajax总结
- 如何在react使用ajax?
- 组件内的通信
-
- 父组件传子组件
- 子组件传父组件
- 兄弟组件互传
- But
前言
该项目是用于分析兰州疫情的一款软件,后端和数据收集及分析都有相应人员负责,我负责整个前端部分,由于一开始引以为傲地以为修改过几个html界面,以为前端不过如此了,直到导师告诉我需要用react去开发时…哭了,react是什么都没有听说过,于是开始了边学边写代码的过程,本博客用以记录整个开发遇到的坎…
react介绍(觉得这些官方介绍啰嗦的直接看个人总结)
一、React的起源和发展
React起源于Facebook的内部项目,因为该公司对当时市场上的所有javascript MVC 框架都不满意,所以就决定自己搞一套框架,当时的初心是用来架构自己公司的Instagram(图片分享)。搞出来之后,感觉这套东西还不错,在2013年5月很快就开源了。
二、React的出发点
基于HTML的前端界面开发正变得越来越复杂,其本质问题基本都可以归结于如何将来自于服务器端或者用户输入的动态数据高效的反映到复杂的用户界面上。而来自Fackbook的React框架正是完全面向此问题的一个解决方案,按官网描述,其出发点为:用于开发数据不断变化的大型应用程序(Building large applications with data that changes over time)。相比传统型的前端开发,React开辟了一个相当于相当另类的途径,实现了前端界面的高性能高效率开发。
三、Recat与传统MVC的关系
轻量级的视图层库! A JavaScript library for building user interfaces
React不是一个完整的MVC框架,最多可以认为是MVC中的(view)层,甚至React并不非常认可MVC开发模式;React构建页面UI的库。可以简单地理解为,React将界面分成了各个独立的小块,每一个块就是组件,这些组件之间可以组合、嵌套、就成了我们的页面
四、React高性能的体现:虚拟DOM
原理:在我们的开发过程中,我们需要将变化莫测的数据实时的反映到UI上,这时就需要对DOM进行操作。但频繁或复杂的操控DOM会产生许多性能上的问题。————如何进行高性能的复杂DOM操作通常是衡量一个前端开发人员技术的重要指标。
React为此引入了虚拟DOM(virtual DOM)的机制:在浏览器端用javascript实现了一套DOM API。基于React进行开发时所有的DOM构造都是通过虚拟DOM进行,每当数据变化时,React都会重新渲染整个DOM树,然后React将当前整个DOM树和上一次的DOM树进行对比,得到DOM结构的的区别,然后仅仅需要变化的部分进行实际的浏览器DOM更新。而且React能够批量处理虚拟DOM的刷新,在一个事件循环(Event loop)内的两次数据变化被合并,例如你连续的先将节点内容从A-B,B-A,React会认为A变成B,然后又从B变成A UI不发生任何变化,而如果通过手动控制,这种逻辑通常是极其复杂的
React Fiber:
在React16之后发布的一种react核心算法,React Fiber是对核心算法的一次重新实现(官网说法)。之前用的是diff算法
在之前的React中,更新过程是同步的,这可能会导致性能问题。
当React决定要加载或者更新组件树时,会做很多事,比如调用各个组件的生命周期函数,计算和比对Virtual DOM,最后更新DOM树,这整个过程是同步进行的,也就是说只要一个加载或者更新过程开始,中途不会中断。因为javascript单线程的特点,如果组件树很大的时候,每个同步任务耗时太长,就会出现卡顿。
React Fiber的方法其实很简单——分片。把一个耗时长的任务分成很多小片,每一个小片的运作时间很短,虽然总时间依然很长,但是在每个小片执行完之后,都给其他任务一个执行的机会,这样唯一的线程就不会被独占,其他任务依然有运行的机会。
五、React的特点和优势
1、虚拟DOM
我们之前操作DOM的方式是通过document.getElementById()的方式,这样的过程实际上是先去读取html的dom结构,将结构转换成变量,而进行操作
而reactjs定义了一套变量形式的dom模型,一切操作和换算直接在变量中,这样减少了操作真实dom,性能真实相当的高,和主流MVC框架有什么本质区别,并不和dom打交道
2、组件系统
react最核心的思想是将页面中任何一个区域或者元素都可以看做一个组件component
那什么是组件呢?
组件指的就是同时包含了html、css、js、image元素的聚合体
3、单向数据流
其实reactjs的核心内容就是数据绑定,所谓数据绑定指的是只要将一些服务端的数据和前端页面绑定好,开发者只关注实现业务就行了
4、JSX语法
在vue中,我们使用render函数来构建组件的dom构造性能较高,因为省去了查找和编译模板的过程,但是在render中利用createElement创建结构的时候代码可读性较低,较为负载,此时可以利用JSX语法来在render中创建dom,解决了这个问题,但是前提是需要使用工具编译jsx
个人总结
简单的说,react就是将每个对象做成了一个个的组件(知道面向对象的就很容易理解啦),这一个个组件可以理解为一个个对象,就像一个杯子,一台电脑,将一个个组件写好之后,需要用的时候拿出来调用就好了。例如一个桌子是一个组件,你可以在这个杯子里放一个杯子,一台电脑,然后因为杯子也是对象,你可以在杯子里放水,也可以放牛奶。如果还不太明白,就可以看一下我们是怎么去构建一个组件的。
项目初试
如果习惯看文档的,可以直接跟着官方文档学习,效果更佳哦
中文官方文档.
英文官方文档.
那么我们开始第一个简单的项目(ps:注意初学者这里一定要跟着教程来,不然自己慢慢摸索会浪费大量的时间)
首先在终端下安装create-react-app
npm install -g create-react-app
初始化项目
create-react-app my-new-app
切换目录
cd my-new-app
初始化项目
npm init
导入react和react-dom
npm install --save react react-dom
然后输入
yarn start
项目就可以运行起来了,如图溜浏览器打开这个界面就表示可一切顺利,可以接下来的工作了。
如图这是我之前遇到的一种报错,原因是node的版本太低,把node版本往上调一调便可以了。
我们现在写一个简单的组件
我们先打开index.jsx文件,我们发现了一个App,其实这就是一个组件,render()是一个渲染方法,我们将APP写在render里代表这是我们需要渲染的组件,换言之,我们在这里面放入了什么内容,在界面上我们也就相应看见什么内容。(为了方便后续的内容讲解,需要导入的文件我都没有删除)
我们打开App组件内看一下里面有什么东西,然后我们运行一下是什么效果。我们同样在render()方法里我们写了一个div标签。
会出现什么效果呢?
没错,render里面写了什么东西,网页也就相应渲染什么东西,我们现在想办法写一个导航栏。
import logo from './logo.svg';import './App.css';import React, { useState } from 'react'import { AppOutline, MessageOutline, MessageFill, UnorderedListOutline, UserOutline,} from 'antd-mobile-icons'import Path from './Path';import Community from './Community';import Information from './Information';import News from './News';// import Xinjian from './xinjian'//类名首字母大写且与文件名一致class App extends React.Component{ state={ tabs:['确诊小区','确诊人员信息','确诊人员路径'], num:0 } //生命周期函数(渲染) render(){ let Shower if (this.state.num===1) { Shower=(<News />) } return( <div> <ul> <div className="div-a">兰州疫情可视化</div> { this.state.tabs.map((item,index)=>( <li className={this.state.num===index ? 'active':''} key={index} onClick={this.liClickFn.bind(this,index)} >{item}</li>) )} </ul> {Shower} <ol> <div className='div-b'> { this.state.num==0 ? <div className='div-b' > {/* <Community/> */} </div> :this.state.num==1?<div className='div-b'> {/* <Information /> */}</div> :<div className='div-b'> {/* <Path /> */}</div>} </div> </ol> </div> </> ) } liClickFn(index){ console.log(index) this.setState({num:index}) }}export default App;
在这里我偷了一个懒,通过一个很奇妙的关系完成了一个route的工作,实在没办法,我的电脑不知道路由功能总是实现报错,于是通过选择判断的方式很好的实现了这一功能,还好后续的很多功能不需要用到路由,不然我还得在这上面花费大量时间去研究。
这样甚好了,我们继续,目前可能有的朋友会注意到我在上诉代码中注释了三个组件
<div className='div-b'> { this.state.num==0 ? <div className='div-b' > {/* <Community/> */} </div> :this.state.num==1?<div className='div-b'> {/* <Information /> */}</div> :<div className='div-b'> {/* <Path /> */}</div>}
没错,这里便是我写的类似APP的组件,通过判断语句我们实现了点击不同的导航进入不同的组件。我们已经知道组件之间的关系以及怎么使用后接下来我们来介绍该项目的一些核心功能与技术的实现。
核心功能及技术
地图导入
我们首先可以进入高德地图API.申请一个key
使用npm进行安装react-amap库
npm i react-amap
然后我们写一个Address组件(文件名自取,但文件名一般要与类名一致)
import React,{Component} from 'react'import {Map,Polygon} from 'react-amap'import { InfoWindow } from 'react-amap'const mapKey = '1234567809843asadasd' //需要自己去高德官网上去申请class Address extends Component {render(){return (<div style={{width: '100%', height: '500px'}}> <Map amapkey={mapKey} zoom={15}center={[103.823557, 36.058039]}> </Map> {/* 原始界面 */}{/* <Map amapkey={mapKey} zoom={15}> <Polygon path={this.state.path} bubble={this.state.bubble} style={{strokeColor:'red'}}/></Map> */}</div>)}}export default Address
具体代码功能实现原理十分简单,就是使用了react-amap库里面的一个Map组件,然后将自己申请的key导入便可以显示在线地图啦。当然还有一些东西需要阁下自己去研究,将一个小区的范围标识出来,或者显示一条路径,其实也就是一个个组件,我将源代码留下供各位研究:
import React,{Component} from 'react'import {Map,Polygon} from 'react-amap'import { InfoWindow } from 'react-amap'const mapKey = '1234567809843asadasd' //需要自己去高德官网上去申请class Address extends Component {constructor (props) { super (props) this.state = {path: [ [103.73445,36.053306], [103.734732,36.053904], [103.734748,36.053927], [103.734771,36.053939], [103.734801,36.053944], [103.734856,36.053942], [103.734927,36.053929], [103.735662,36.053715], [103.736493,36.05348], [103.737362,36.053239], [103.737383,36.053226], [103.737394,36.05321], [103.737394,36.053181], [103.737261,36.052589], [103.737261,36.052558], [103.737261,36.052549], [103.737265,36.052536], [103.73727,36.052527], [103.737309,36.052518], [103.738181,36.052386], [103.738814,36.052288], [103.738831,36.052293], [103.738842,36.052305], [103.738849,36.052321], [103.738956,36.052734], [103.739029,36.053069], [103.739145,36.053473], [103.739247,36.053652], [103.739333,36.053748], [103.739352,36.053755], [103.739377,36.053761], [103.739617,36.053795], [103.739648,36.0538], [103.740186,36.054173], [103.740206,36.0542], [103.740237,36.054486], [103.740252,36.0546], [103.740258,36.05466], [103.74027,36.054725], [103.740289,36.05476], [103.740508,36.05504], [103.740521,36.055089], [103.740516,36.055125], [103.74051,36.055147], [103.740339,36.055321], [103.740041,36.055443], [103.740037,36.055458], [103.740036,36.055476], [103.740042,36.055493], [103.74028,36.055993], [103.740535,36.056494], [103.740576,36.056537], [103.740602,36.056547], [103.740651,36.056548], [103.740737,36.056544], [103.741223,36.056372], [103.74124,36.056353], [103.74124,36.056328], [103.741093,36.056054], [103.741102,36.056031], [103.74111,36.056027], [103.741125,36.05602], [103.741141,36.056019], [103.742148,36.056333], [103.742164,36.056341], [103.742186,36.056348], [103.742324,36.0567], [103.74227,36.056975], [103.742282,36.057043], [103.742384,36.057305], [103.742384,36.057322], [103.742364,36.057452], [103.742273,36.057623], [103.741921,36.057889], [103.741892,36.057952], [103.741884,36.057991], [103.741884,36.058031], [103.741888,36.058073], [103.741916,36.058157], [103.742023,36.058357], [103.742023,36.058376], [103.742022,36.058389], [103.742009,36.058401], [103.741988,36.058401], [103.74139,36.058567], [103.740335,36.05885], [103.740315,36.058862], [103.740313,36.058868], [103.740312,36.05889], [103.740464,36.059233], [103.740458,36.059253], [103.740439,36.059264], [103.740029,36.059405], [103.73892,36.059762], [103.737878,36.060113], [103.737004,36.060429], [103.736073,36.060711], [103.735131,36.061007], [103.733878,36.061393], [103.732241,36.061949], [103.73221,36.061948], [103.732188,36.06194], [103.732178,36.061925], [103.732018,36.061608], [103.73131,36.060164], [103.730264,36.058039], [103.729089,36.055563], [103.728906,36.055208], [103.728903,36.055187], [103.728903,36.055164], [103.728912,36.055132], [103.729241,36.054822], [103.730134,36.05394], [103.731046,36.053022], [103.731678,36.052398], [103.732494,36.051565], [103.732513,36.051541], [103.73252,36.051526], [103.73252,36.051511], [103.73252,36.051488], [103.732154,36.050789], [103.731868,36.050215], [103.731873,36.050194], [103.731887,36.050182], [103.731943,36.050182], [103.73298,36.05039], [103.73306,36.050517], [103.733253,36.050896], [103.73445,36.053306] ],bubble: true, visible: true, position: { longitude: 103.73306, latitude: 36.050517 } } }render(){return (<div style={{width: '100%', height: '500px'}}> <Map amapkey={mapKey} zoom={15}center={[103.823557, 36.058039]}> <Polygon path={this.state.path} bubble={this.state.bubble} style={{strokeColor:'red'}} /> <InfoWindow center={this.state.position} position={this.state.position} visible={this.state.visible} size={this.state.size} > <b>兰州理工大学</b> </InfoWindow></Map> {/* 原始界面 */}{/* <Map amapkey={mapKey} zoom={15}> <Polygon path={this.state.path} bubble={this.state.bubble} style={{strokeColor:'red'}}/></Map> */}</div>)}}export default Address
现在地图功能已经基本能显示了,我们现在已经没时间去自己设计一个时间轴了,于是老师建议我在网上找一个react时间轴,在这里不得不说GitHub牛逼,GitHub上面有很多优质开源的时间轴。
开源组件的利用(时间轴)
时间轴感觉没有什么东西可以分享,尽管在这研究上我花时间最多,但主要是因为没有系统学习过导致里面很多逻辑不理解而导致浪费了大量时间,在这里我的建议是先将整个时间轴导入你所定义的一个组件,然后不断去调试去发现里面的逻辑,明白每一段代码的具体功能后删除不需要的功能,然后完善需要的功能。这里我只附代码和图片。
import React, { useEffect, useState } from 'react';// import ReactDOM from 'react-dom';import G6 from '@antv/g6';// // eslint-disable-next-lineclass Timeline extends React.Component { constructor(props) { super(props); this.state = { data: { nodes: [], edges: [], } }; } componentDidMount() { console.log(11) const data = { nodes: [], edges: [], }; for (let i = 1; i < 60; i++) { const id = `node-${i}`; const month = i < 30 ? '01' : '02'; const day = i % 30 < 10 ? `0${i % 30}` : `${i % 30}`; console.log(1) data.nodes.push({ id, date: parseInt(`2020${month}${day}`), value: Math.round(Math.random() * 300), label: parseInt(`2020${i}`), }); data.edges.push({ source: `node-${Math.round(Math.random() * 60)}`, target: `node-${Math.round(Math.random() * 60)}`, }); } console.log(2) const timeBarData = []; for (let i = 1; i < 60; i++) { const month = i < 30 ? '01' : '02'; const day = i % 30 < 10 ? `0${i % 30}` : `${i % 30}`; timeBarData.push({ date: parseInt(`2021${month}${day}`), value: Math.round(Math.random() * 300), }); }console.log(3) const container = document.getElementById('container'); console.log(container) const width = container.scrollWidth; const height = 0 // console.log('容器',container); // console.log(typeof scrollWidth); const nodeSize = 20; let count = 0; const timebar = new G6.TimeBar({ x: 0, y: 0, width, height: 150, padding: 10, type: 'tick', tick: { data: timeBarData, width: width, height: 42, padding: 2, tickLabelFormatter: (d) => { count++; const dateStr = `${d.date}`; if ((count - 1) % 10 === 0) { return `${dateStr.substr(0, 4)}-${dateStr.substr(4, 2)}-${dateStr.substr(6, 2)}`; } return false; }, tooltipFomatter: (d) => { const dateStr = `${d}`; return `${dateStr.substr(0, 4)}-${dateStr.substr(4, 2)}-${dateStr.substr(6, 2)}`; }, }, }); // constrained the layout inside the area const constrainBox = { x: 10, y: 10, width: 580, height: 450 }; const onTick = () => { let minx = 99999999; let maxx = -99999999; let miny = 99999999; let maxy = -99999999; data.nodes.forEach((node) => { if (minx > node.x) { minx = node.x; } if (maxx < node.x) { maxx = node.x; } if (miny > node.y) { miny = node.y; } if (maxy < node.y) { maxy = node.y; } }); const scalex = (constrainBox.width - nodeSize / 2) / (maxx - minx); const scaley = (constrainBox.height - nodeSize / 2) / (maxy - miny); data.nodes.forEach((node) => { node.x = (node.x - minx) * scalex + constrainBox.x; node.y = (node.y - miny) * scaley + constrainBox.y; }); }; const graph = new G6.Graph({ container: 'container', width, height, linkCenter: true, plugins: [timebar], layout: { type: 'force', preventOverlap: true, onTick, }, defaultNode: { size: nodeSize, type: 'circle', style: { fill: '#DEE9FF', stroke: '#5B8FF9', }, }, modes: { default: ['drag-node'], }, }); graph.data(data); // graph.render(); if (typeof window !== 'undefined') window.onresize = () => { if (!graph || graph.get('destroyed')) return; if (!container || !container.scrollWidth || !container.scrollHeight) return; graph.changeSize(container.scrollWidth, container.scrollHeight - 100); }; } componentWillUnmount() { } render() { return ( <div className='Timeline' id="container"> {/* <Timeline></Timeline> */} </div> ) }}export default Timeline;
这也是我开发的整个逻辑,一些简单的部分我没有写是因为通过看简单的几个博客便可实现其功能,笔者写的是在完全没有过前端开发的基础上,遇到的一些困难性难题,有些难题笔者研究了一周以上,例如下面的ajax及组件内通信的实现。
Ajax与postman的结合使用
一开始也没有听说过ajax是何方神圣,只知道这名字听着确实是很高大上(阿贾克斯)。
于是对此进入了深入研究,我在写如何使用之前还是copy一些ajax的简历让大家有一个初步的认识。
Ajax是什么?
Ajax 的全称是asynchronous javascript and xml
从全称不难发现AJAX = 异步,JavaScript 和 XML。
Ajax 并不算是一种新的技术,而是已有技术的组合,主要用来实现客户端与服务器端的异步通信效果,实现页面的局部刷新。
为什么需要Ajax
在没有使用Ajax情况下:Web站点强制用户进入提交/等待/重新显示(重新加载整个网页),对于服务器加大了流量,对于用户每一次与服务器传递数据都会重新加载整个网页。
使用Ajax情况下:通过在后台与服务器进行少量数据交换,AJAX 使用JavaScript和DHTML立即更新UI(部分刷新),这对用户有利,因为它不会干扰或中断他或她正在使用的网页。
ajax总结
ajax可以实现不需要click页面更新的情况下,将鼠标放置某一位置便可实现前端与后端数据的交互。简单的说,就是你把鼠标放在一个地方,就可以实现往后端发送数据并且接受数据然后显示出来
如何在react使用ajax?
其实如果看网上很多代码显示的特别累赘,经过多次测试与总结,发现无论在哪个组件里,需要向后端请求数据时便可写以下数据:
componentDidMount(){ // 在这里进行Ajax数据请求,axios,fetch,jquery Ajax或者request都可以 axios.get(this.baseUrl) .then(res => { // console.log('123212313212') const goodlists= res.data;this.setState({path_a: res.data.data }) // console.log(this.state.path_a) }) .catch(err => { console.log(err); }) }
当然要记得安装和导入axios的库呀!!!!!!!!!!!!!!
然后如果需要请求后端的数据,便可copy这段代码,将要请求的url设置为this.baseUrl即可(后面会给源码),很简单,请求了url后后端会返回一个json文件,这里我们可以使用这个网站.来模拟后端,当数据请求下来后慧存在res里,我们只需要将res.data赋值给相关变量即可。我每一个界面的页面请求都是在这个代码的基础上修改的即可,当然也需要上述网站的配合使用,效果更佳。
postman就是一个请求网页的一个工具,通过请求url看你写的后端json格式是否合适,配合着使用让错误无处可逃。
还是附一下我这个组件的代码吧~
import React, { Component } from 'react'// import Address from './Address'import Timeline from './TimeBar'// import React,{Component} from 'react'import {Map,Polygon,Polyline } from 'react-amap'import { InfoWindow } from 'react-amap'import axios from 'axios';export default class Path extends Component { render() { return ( <div> <Address /> {/* <Timeline /> */} </div> ) }}const mapKey = '1234567809843asadasd' //需要自己去高德官网上去申请class Address extends Component {constructor (props) { super (props) this.baseUrl='https://www.fastmock.site/mock/48066ada0aaafe077341ea0f40b70986/react-lanzhou/address' this.state = { path_a: [], bubble: true, visible: true, position: { longitude: 103.73306, latitude: 36.050517 } } }render(){return (<div style={{width: '100%', height: '500px'}}> <Map amapkey={mapKey} zoom={9}center={[103.214142,36.3232414]}> {/* <Polygon path={this.state.path} bubble={this.state.bubble} style={{strokeColor:'red'}} /> */}{ this.state.path_a.map((item) => {return ( <Polyline path={item} />) }) } {/* <Polyline path={this.state.path_a} /> */} <InfoWindow center={this.state.position} position={this.state.position} visible={this.state.visible} size={this.state.size} > <b>兰州理工大学</b> </InfoWindow></Map> {/* 原始界面 */}{/* <Map amapkey={mapKey} zoom={15}> <Polygon path={this.state.path} bubble={this.state.bubble} style={{strokeColor:'red'}}/></Map> */}</div>)} componentDidMount(){ // 在这里进行Ajax数据请求,axios,fetch,jquery Ajax或者request都可以 axios.get(this.baseUrl) .then(res => { // console.log('123212313212') const goodlists= res.data;this.setState({path_a: res.data.data }) // console.log(this.state.path_a) }) .catch(err => { console.log(err); }) }}
组件内的通信
页面的所有功能基本实现了,当数据从后端传到前端时,有时候数据需要从一个组件到另外一个组件。我们一开始就介绍了react的数据流是单向的,所以这就导致了一个严重的问题,数据流总是只能从一个组件到另外一个组件,如果两个组件是父子关系还好,如果是兄弟关系,还得找到他们之间相关的组件,从而一步步传过去(当然也有很好的方式用redux来管理数据,但我开发时间有限,我就先用最easy的方式实现了)
父组件传子组件
父组件当有数据要给子组件时,父组件可以直接通过调用子组件时传参的方式传输数据,例如我们要传输this.state
在子组件通过props便可接受到数据并赋值给子组件的变量。
然后在子组件中便可使用父组件传输过来的变量了!
子组件传父组件
当数据是在子组件中需要传输到父组件中时,我们需要在父组件中定义一个方法,将该方法传给子组件,在子组件中使用父组件中的方法,通过这种方式来传输数据。
然后在子组件中调用父组件方法。这里通过将后端请求的数据传给父组件。
兄弟组件互传
这里就很简单啦,我们通过找到两个组件的相关组件后,将数据一步步传下去就行了,例如上面的组件Timeline将后端数据请求到之后然后传给父组件Xiaoqu,然后父组件Xiaoqu将数据传给Address,如此就实现了两个兄弟组件之间的通信。
But
兄弟组件之间通信还需要关注一个很重要的知识点,那就是react的生命周期。如果不注意生命周期的利用,很容易导致哪怕数据传输了(甚至出现不传送),也无法渲染的情况。
尤其还要了解this.setState()的使用,当数据传输后如果直接赋值,是不会让render()重新渲染的,因为对象没有变化吧,所以数据传到另一个组件时一定要用this.setState()。
生命周期的大概顺序是这样,如图:
在数据传输过程中如果生命周期都没搞清楚,也许会遇到很多麻烦。有时间可以参考如下几篇博客吧
链接: https://blog.csdn.net/huanghanqian/article/details/80721575.
链接: https://blog.csdn.net/h200102/article/details/120040743.
链接: https://blog.csdn.net/lixiaonaaa/article/details/111290070.
链接: https://blog.csdn.net/qq_41315539/article/details/105448350.
看完这几篇差不多也应该能懂了。