【React】看完这篇文章能够学会React初级技术

  • 本文是根据链接这个视频系列的笔记做的学习记录整理,讲课风格很有趣(2倍速跟小甲鱼声音很像嘿嘿嘿)
  • 在深入学习react之前建议先看看一些react的思想,相关文章我已经总结出来啦!半小时理解react
  • 学完React之后就可以做一个小小项目啦
  • 到最后就可以开始刷刷面试真题巩固知识啦
  • 相关链接文章都是博主自己整理的哦 还不赶快去三连嘛?( = w =)

    文章目录

    • 1.初识react
      • 特点
      • 创建项目
      • react元素渲染
      • react JSX
      • JSX表达式
        • 相关案例总结
      • react样式和注释
      • react组件
      • react事件状态
        • 例如设置时间
        • 例如改变标签
    • 2.继续认识react
      • 经典父传子数据传递
      • 经典子传父数据传递
      • react事件处理
      • react条件渲染
      • react列表渲染
      • 实践:react制作疫情地图
    • 3.深入理解react
      • 生命周期
      • 表单
      • 案例:配合ajax获取的数据的疫情进展显示
    • 4.插槽、路由
      • react插槽
      • react路由
      • 解构
    • 5.Redux
      • 使用原因和设计理念
      • 三大原则
      • 概念解析
      • 步骤:

1.初识react

构建用户界面的JavaScript库,主要用于构建UI界面

特点

1、声明式的设计
2、高效,采用虚拟DOM来实现DOM的渲染,最大限度的减少DOM的操作。
3、灵活,跟其他库灵活搭配使用。
4、JSX,俗称JS里面写HTML,JavaScript语法的扩展。
5、组件化,模块化。代码容易复用,2016年之前大型项目非常喜欢react
单向数据流。没有实现数据的双向绑定。数据-》视图-》事件-》数据

创建项目

  1. 安装脚手架Create React App。
    或者npm
cnpm install -g create-react-app
  1. 创建项目
create-react-app 01reactapp(项目名称可以自定义)

macOS不行就npm init react-app my-app

  • 如果npm run start失败就看这个
  • 首先,初始化项目,一路回车就行
npm init -f

接着安装依赖

npm install formidable --save

-f表示force的意思,不加这个,npm会让你输入一堆信息,比如name、version之类,如果只是做做实验小demo,直接-f,npm帮你初始化package.json,并填充各种默认值,省事。
–save表示将安装的包加入依赖列表的意思,可以看下package.json 里的dependencies字段。后面再运行 npm install,就会把所有依赖安装下来。如果不加–save,什么都不会安装。
顺便如果速度慢可以设置为淘宝 npm 镜像

npm config set registry https://registry.npm.taobao.org

react元素渲染

//实现页面时刻的显示

function clock(){
    let time = new Date().toLocaleTimeString()
    let element = (
        <div>
            <h1>现在的时间是{time} </h1>
            <h2>这是副标题</h2>
        </div>
    )
let root = document.querySelector('#root');
//getbyid...也可以
    ReactDOM.render(element,root)
}

clock()

setInterval(clock,1000)

函数式渲染

function Clock(props){
    return (
                <div>
                    <h1>现在的时间是{props.date.toLocaleTimeString()} </h1>
                    <h2>这是函数式组件开发</h2>
                </div>
    )
}

function run(){
    ReactDOM.render(
        <Clock date={new Date()} />,
        document.querySelector('#root')
    )
}

setInterval(run,1000)

react JSX

优点:
1、JSX执行更快,编译为JavaScript代码时进行优化
2、类型更安全,编译过程如果出错就不能编译,及时发现错误
3、JSX编写模板更加简单快速。(不要跟VUE比)

注意:
1、JSX必须要有根节点。
2、正常的普通HTML元素要小写如果是大写,默认认为是组件

JSX表达式

1、由HTML元素构成
2、中间如果需要插入变量用{}
3、{}中间可以使用表达式
4、{}中间表达式中可以使用JSX对象
5、属性和html内容一样都是用{}来插入内容

相关案例总结

import React from 'react';
import ReactDOM from 'react-dom';
import './App.css'
/* ./App.css
	.bgRed{
	  background-color: red;
	}
*/
let time = new Date().toLocaleTimeString()
let str = '当前时间是:'
let element = (
    <div>
        <h1>helloworld</h1>
        <h2>{str+time}</h2>
    </div>
)

console.log(element)

let man = '发热';
let element2 = (
    <div>
        <h1>今天是否隔离</h1>
        <h2>{man=="发热"?<button>隔离</button>:"躺床上"}</h2>
    </div>
)

//let man = '发热';
let element4 = (
    <div>
        <span>横着躺</span>
        <span>竖着躺</span>
    </div>
)
man = '正常'
let element3 = (
    <div>
        <h1>今天是否隔离</h1>
        <h2>{man=="发热"?<button>隔离</button>:element4}</h2>
    </div>
)

let color = 'bgRed'
let logo = 'https://www.baidu.com/img/pc_1c6e30772d5e4103103bd460913332f9.png'
//HTML的样式类名要写className,因为class在js当中是关键词
let element5 = (
    <div className={color}>
        <img src={logo} />
        红色的背景颜色
    </div>

)

ReactDOM.render(
    element5,
    document.getElementById('root')

)

react样式和注释

import React from 'react';
import ReactDOM from 'react-dom';
import './04style.css';

//JSX中设置style
let exampleStyle = {
    background:"skyblue",
    borderBottom:"4px solid red",
    'background-image':"url(https://www.baidu.com/img/pc_1c6e30772d5e4103103bd460913332f9.png)"
}

let element = (
    <div>
        <h1 style={exampleStyle}>helloworld</h1>
    </div>
)

let classStr = "redBg"
let element2 = (
    <div>
        <h1 className={"abc "+classStr}>helloworld</h1>
    </div>
)

let classStr2 = ['abc2','redBg2'].join(" ")
let element3 = (
    <div>
        {/* 只能这样写注释 */}
        {/* 这里实现了name和style一起装入,同时style还有个导入文件来渲染 */}
        <h1 className={classStr2} style={exampleStyle}>helloworld</h1>
    </div>
)



ReactDOM.render(
    element3,
    document.querySelector('#root')
)
  • element中的style必须是个对象属性,字符串等等要报错,
    就比如这样

    helloworld


    就必须

    helloworld

  • element中的className合并是这样写:

    helloworld

  • 如果className多个类用外部数组传入的话,会直接连接这个数组
let classStr2 = ['abc2','redBg2']
let element3 = (
    <div>
        {/* 这里写注释 */}
        <h1 className={classStr2}>helloworld</h1>
    </div>
)

返回的DOM为

helloworld


如果要合并的话就用 ['abc2','redBg2'].join(' ')返回abc2 redBg2,然后再接受类选择器的渲染:

  • 下面为外部css文件的导入,最开始import './04style.css';就行
.abc2{
    width: 200px;
    height: 200px;
}

.redBg2{
    background: orangered;
}

react组件

import React from 'react';
import ReactDOM from 'react-dom';
import './04style.css';


//函数式组件
function Childcom(props){
    console.log(props)

    let title = <h2>我是副标题</h2>
    let weather = props.weather
    //条件判断 
    let isGo = weather=='下雨' ?"不出门":"出门"

    return (
        <div>
            <h1>函数式组件helloworld</h1>
            {title}

            <div>
                是否出门?
                <span>{isGo}</span>
            </div>
        </div>
    )
}


//类组件定义
class HelloWorld extends React.Component{
    render(){
        console.log(this)
        return (
            <div>
                <h1>类组件定义HELLOWORLD</h1>
                <h1>hello:{this.props.name}</h1>
                <Childcom weather={this.props.weather} />
            </div>
        )
    }
}

// ReactDOM.render(
//     ,
//     document.querySelector('#root')
// )

ReactDOM.render(
    <HelloWorld name="xx" weather="下雨" />,
    document.querySelector('#root')
)
  • 函数式组件(静态)参数传入props(,)直接返回DOM,不用render但是内部可以有{}相关的渲染,️state事件!!!
  • 类组件参数(动态)直接按照类相关的参数传入,继承React.Component渲染render,render内部渲染是获取props和state相关的操作,然后返回渲染后的DOM。
class XxxXxx extends React.Component{
    render(){
        return (
            <div>
                <h1>类组件定义HELLOWORLD</h1>
                <h1>hello:{this.props.name}</h1>
                <Childcom weather={this.props.weather} />
            </div>
        )
    }
}
  • 类组件render渲染内部还可以有其他组件,调用方式就是在虚拟DOM表达式里面引入即可,例如:【React】看完这篇文章能够学会React初级技术_第1张图片
    上述组件叫做复合组件。

react事件状态

例如设置时间

import React from 'react';
import ReactDOM from 'react-dom';

class Clock extends React.Component{
    constructor(props){
        super(props)
        //状态(数据)--》view
        //构造函数初始化数据,将需要改变的数据初始化到state中
        this.state = {
            time:new Date().toLocaleTimeString()
        }
        //console.log(this.state.time)
    }

    render(){
        //console.log("这是渲染函数")
        //this.state.time = new Date().toLocaleTimeString();
        return (
            <div>
                <h1>当前时间:{this.state.time}</h1>
            </div>
        )
    }

    //生命周期函数,组件渲染完成时的函数
    componentDidMount(){
        setInterval(()=>{
            console.log(this.state.time)
            //this.state.time = new Date().toLocaleTimeString(); //错误的方式
            //切勿直接修改state数据,直接state重新渲染内容,需要使用setState
            //通过this.setState修改完数据后,并不会立即修改DOM里面的内容,react会在,
            //这个函数内容所有设置状态改变后,统一对比虚拟DOM对象,然后在统一修改,提升性能。
            //小程序也是也是借鉴REACT状态管理操作
            this.setState({
                time:new Date().toLocaleTimeString()
            })
            
        },1000)
    }

}

ReactDOM.render(
    <Clock />,
    document.querySelector('#root')
)
  • 一个bug的解析:
import React from 'react';
import ReactDOM from 'react-dom';
class Clock extends React.Component{
	constructor(props){
		super(props)
		this.state = {
			time:new Date().toLocalTimeString()
		}
		//这里校验下有没有渲染成功
		console.log(this.state.time)
	}
	render(){
		return(
			<div><h1>当前时间:{this.state.time}</h1></div>
		)
	}
}
ReactDOM.render(
    <Clock />,
    document.querySelector('#root')
)
setInterval(()=>{
	ReactDOM.render(
		<Clock />,
    	document.querySelector('#root')
    )
    console.log(new Date())
},1000)

在这里将会显示的是,第一次render的时间,和后面表示进入setInterval计时器的时间,但是没有显示再次渲染的时间,因为在外部反复渲染同一个组件是不需要再次初始化的,只有内部state的事件导致数据改变了才重新渲染

【React】看完这篇文章能够学会React初级技术_第2张图片
解决方式:用我们的componentDidMount(){}来进行渲染后的后续操作【React】看完这篇文章能够学会React初级技术_第3张图片

  • 再定义一个state事件componentDidMount(),渲染之后的操作都要用this.setState哦~
componentDidMount(){
        setInterval(()=>{
            console.log(this.state.time)
            //this.state.time = new Date().toLocaleTimeString(); //错误的方式
            //切勿直接修改state数据,直接state重新渲染内容,需要使用setState
            //通过this.setState修改完数据后,并不会立即修改DOM里面的内容,react会在,
            //这个函数内容所有设置状态改变后,统一对比虚拟DOM对象,然后在统一修改,提升性能。
            //小程序也是也是借鉴REACT状态管理操作
            this.setState({
                time:new Date().toLocaleTimeString()
            })
            
        },1000)
    }

例如改变标签

import React from 'react';
import ReactDOM from 'react-dom';
import './Tab.css'


class Tab extends React.Component{
    constructor(props){
        super(props)

        //设置状态、数据
        this.state = {
            c1:'content active',
            c2:"content"
        }

        this.clickEvent = this.clickEvent.bind(this)
    }
    clickEvent(e){
        console.log('clickEvent')
        console.log(e.target.dataset.index)
        //target 事件属性可返回事件的目标节点(触发该事件的节点),如生成事件的元素、文档或窗口。
        let index = e.target.dataset.index;
        console.log(this)
        if(index=='1'){
            this.setState({
                c1:'content active'
               
            })
            this.setState({
                c2:"content"
            })
        }else{
            this.setState({
                c1:'content',
                c2:"content active"
            })
        }
    }

    render(){
        return (
            <div>
                <button data-index="1" onClick={this.clickEvent}>内容一</button>
                <button data-index="2" onClick={this.clickEvent}>内容二</button>
                <div className={this.state.c1}>
                    <h1>内容1</h1>
                </div>
                <div className={this.state.c2}>
                    <h1>内容2</h1>
                </div>
            </div>
        )
    }
}
ReactDOM.render(
    <Tab />,
    document.querySelector('#root')
)
  • 构造函数外的事件都要绑定到构造函数内部哦~方便this操作哦~
this.clickEvent = this.clickEvent.bind(this)

2.继续认识react

经典父传子数据传递

  • 通过setState进行的子元素数据传递
import React from 'react';
import ReactDOM from 'react-dom';
import './01props.css'


//在父元素中使用state去控制子元素props的从而达到父元素数据传递给子元素

class ParentCom extends React.Component{
    constructor(props){
        super(props)
        this.state = {
            isActive:true
        }
        this.changeShow = this.changeShow.bind(this)
    }

    render(){
        return (
            <div>
                <button onClick={this.changeShow}>控制子元素显示</button>
                <ChildCom isActive={this.state.isActive} />
            </div>
        )
    }

    changeShow(){
        this.setState({
            isActive:!this.state.isActive
        })
    }
}

class ChildCom extends React.Component{
    constructor(props){
        super(props)
    }
    render(){
        let strClass = null;
        if(this.props.isActive){
            strClass = ' active'
        }else{
            strClass = ""
        }
        strClass = this.props.isActive?" active":"";

        return (
            <div className={"content"+strClass}>
                <h1>我是子元素</h1>
            </div>
        )
    }
}


ReactDOM.render(
    <ParentCom />,
    document.querySelector("#root")
)

经典子传父数据传递

本节完成的是:按子元素渲染的按钮就可以改变父元素的数据。

  • 通过父元素render里的,来传入父元素的setChildData,然后通过childCom来修改传入数据,返回到父元素的render里面后又重新渲染。
  • 这里要注意的是this和箭头函数的操作,传入的是父元素的this,然后给child后child修改的也是child的this(相当于是父元素的this.setChildData),因为涉及到类的this所以就要用箭头函数
import React from 'react';
import ReactDOM from 'react-dom'

//子传父
class ParentCom extends React.Component{
    //构造函数 继承React.Component的props
    constructor(props){
        //props
        super(props)
        //state
        this.state = {
            childData:null //一开始是没有的
        }

    }
    //state
    setChildData = (data)=>{
        this.setState({
            childData:data
        })
    }

    render(){
        return(
            <div>
                {/* this.state.childData 才能获取数据 */}
                <h1>子元素传递给父元素的数据{this.state.childData}</h1>
                {/* 一开始是没有的,因为父元素的childData为null,但是后面用子元素给父元素 */}
                <ChildCom setChildData = {this.setChildData}/>
            </div>
        )
    }
}
class ChildCom extends React.Component{
    constructor(props){
        super(props)
        //state
        this.state = {
            msg:"helloworld"
        }
    }

    //state

    //用箭头函数绑定当前childcom类的this
    sendData = () => {
        console.log(this.state.msg)//测试绑定是否成功
        console.log(this.props)//测试子元素是否拿到父元素的this.setChildData
        this.props.setChildData(this.state.msg)
    }

    //修改数据
    setChildData = (data) =>{
        this.setState({
            childData:data
        })
    }
    render(){
        return(
            
            <div>
                {/* 点击就可以传递数据 */}
                <button onClick={this.sendData}>传递helloworld给父元素</button>
                {/* 或者可以用以下方式 */}
                <button onClick={()=>this.props.setChildData(this.state.msg)}>传递helloworld给父元素</button>
            </div>
        )
    } 
}
ReactDOM.render(
    <ParentCom />,
    // document.getElementById('#root')
    document.querySelector('#root')
)

react事件处理

  1. 特点:
  • react事件,绑定事件的命名,驼峰命名法。
  • {},传入1个函数,而不是字符串
  1. 事件对象:React返回的事件对象是代理的原生的事件对象,如果想要查看事件对象的具体值,必须之间输出事件对象的属性。
  2. 阻止浏览器默认事件
    例如阻止表单提交:
import React from 'react';
import ReactDOM from 'react-dom';

class ParentCom extends React.Component{
    constructor(props){
        super(props)
    }
    render(){
        
        return (
            <div >
                <form action="http://www.baidu.com">
                    <div className="child" >
                        <h1>helloworld</h1>
                        <button onClick={this.parentEvent}>提交</button>
                    </div>

                </form>
                
                {/* 使用ES6箭头函数传递多个参数 */}
                <button  onClick={(e)=>{this.parentEvent1('msg:helloworld',e)}}>提交</button>
                {/* //不使用ES6箭头函数传递多个参数的方式 */}
                <button  onClick={function(e){this.parentEvent1('不使用es6,msg:helloworld',e)}.bind(this)}>提交</button>
            
            </div>
        )
    }
    parentEvent=(e)=>{
        console.log(e.preventDefault)
        e.preventDefault()
        //e.preventDefault()
        //return false; //但是react不行
    }
    parentEvent1 = (msg,e)=>{
        console.log(msg)
        console.log(e)
        
    }
}


ReactDOM.render(
    <ParentCom></ParentCom>,
    document.querySelector("#root")
)

【React】看完这篇文章能够学会React初级技术_第4张图片

以上是原生阻止默认行为的方式,可以直接返回return false
React中,阻止默认必须e.preventDefault();

  1. 参数传递
{/* 使用ES6箭头函数传递多个参数 */}
<button  onClick={(e)=>{this.parentEvent1('msg:helloworld',e)}}>提交</button>
{/* //不使用ES6箭头函数(匿名函数)传递多个参数的方式,就要进行bind(this)的this绑定 */}
<button  onClick={function(e){this.parentEvent1('不使用es6,msg:helloworld',e)}.bind(this)}>提交</button>

.bind(this)是绑定的时候使用的
.call是调用的时候使用的
.apply也是调用时候使用的

react条件渲染

  1. 首先看一个基础的登陆页面条件渲染
    通过在render里面获取state参数的设置,进行条件运算然后返回要渲染的JSX对象
import React from 'react';
import ReactDOM from 'react-dom';

function UserGreet(props){
    return (<h1>欢迎登陆</h1>)
}

function UserLogin(props){
    return (<h1>请先登录</h1>)
}


class ParentCom extends React.Component{
    constructor(props){
        super(props)
        this.state = {
            isLogin:true
        }
    }
    render(){
        if(this.state.isLogin){
            return (<UserGreet></UserGreet>)
        }else{
            return (<UserLogin></UserLogin>)
        }
    }
}


ReactDOM.render(
    <ParentCom></ParentCom>,
    document.querySelector('#root')
)
  • 通过条件运算得出JSX对象,再渲染到模版中。
render(){
        let element = null;
        if(this.state.isLogin){
            element = <UserGreet></UserGreet>;
        }else{
            element = (<UserLogin></UserLogin>);
        }
        return (
            <div>
                <h1>这是头部</h1>
                {element}
                <h1>这是三元运算符的操作</h1>
                {this.state.isLogin?<UserGreet></UserGreet>:<UserLogin></UserLogin>}
                <h1>这是尾部</h1>
            </div>
        )
    }

react列表渲染

  • 基础渲染:将列表内容拼装成数组放置到模板中。将数据拼装成数组的JSX对象。
  • 以下直接用了JSX拼装放置到了模板中
import React from 'react';
import ReactDOM from 'react-dom';

let arr = ["小明","小黑","小白"]

let arrHtml = [<li>小明</li>,<li>小黑</li>,<li>小白</li>];

class Welcome extends React.Component{
    constructor(props){
        super(props)
    }
    render(){
        return (
            <div>
                <ul>
                    {arrHtml}
                </ul>
            </div>
        )
    }
}


ReactDOM.render(
    <Welcome></Welcome>,
    document.querySelector('#root')
)
  • 以下是用了JS的语法,把数组里的JSX对象挨个push进去
import React from 'react';
import ReactDOM from 'react-dom';

class Welcome extends React.Component{
    constructor(props){
        super(props)
        this.state = {
            list:[
                {
                    title:"第一节 React事件",
                    content:"事件内容"
                },
                {
                    title:"第二节 React数据传递",
                    content:"数据传递内容",
                },
                {
                    title:"第三节 条件渲染",
                    content:"条件渲染内容",
                }
            ]
        }
    }

    render(){
        let listArr = [];
        for(let i=0;i<this.state.list.length;i++){
            let item = (
                <li>
                    <h3>{this.state.list[i].title}</h3>
                    <p>{this.state.list[i].content}</p>
                </li>
            )
            listArr.push(item)
        }
        return (
            <div>
                <h1>
                    今天课程内容
                </h1>

                <ul>
                    {listArr}
                    <li>
                        <h3>这是标题</h3>
                        <p>内容</p>
                    </li>
                </ul>

            </div>
        )
    }
}


ReactDOM.render(
    <Welcome></Welcome>,
    document.querySelector('#root')
)
  • 下面是使用数组的map方法:对每一项数据按照JSX的形式进行加工,最终得到1个每一项都是JSX对象的数组,在将数组渲染到模板中。Key值需要放置到每一项中。
  • 静态加工就用函数式:传入props,然后内部return处理好的JSX。但是没法进行事件的处理
function ListItem(props){
    return (
        <li>
            <h3>{props.index+1}:listItem:{props.data.title}</h3>
            <p>{props.data.content}</p>
        </li>
    )
}
  • 动态加工就用类,可以进行事件的处理。例如下面就是定义的类,返回的JSX对象中设置了一个onClick监听事件,对props进行初始化赋值,然后值就传给JSX的标签中(h3的index和title,和后面的p段落),最后进行了一个点击事件的响应。
//动态的就用类
class ListItem2 extends React.Component{
    constructor(props){
        super(props)
    }
    render(){
        return (
            <li onClick={(event)=>{this.clickEvent(
                this.props.index,
                this.props.data.title,
                event
                )}}>
                <h3>{this.props.index+1}:listItem:{this.props.data.title}</h3>
                <p>{this.props.data.content}</p>
            </li>
        )
    }
    clickEvent=(index,title,event)=>{
        alert((index+1)+"-"+title)
    }
}

  • 然后进行外部welcome类的渲染调用。先初始化了state数据,然后渲染时进行ListItem组件的调用传值,为了将这串加到文中;
  • 后面还实现了非组件式的渲染,方法就是获取this.state.list.map((item,index)的值,因为要传值且是对类内部的操作所以要用箭头函数绑定。
class Welcome extends React.Component{
    constructor(props){
        super(props)
        this.state = {
            list:[
                {
                    title:"第一节 React事件",
                    content:"事件内容"
                },
                {
                    title:"第二节 React数据传递",
                    content:"数据传递内容",
                },
                {
                    title:"第三节 条件渲染",
                    content:"条件渲染内容",
                }
            ]
        }
    }

    render(){
        let listArr = this.state.list.map((item,index)=>{
            return (
                <ListItem2 key={index} data={item} index={index}></ListItem2>
                
            )
        })
        
        return (
            <div>
                <h1>
                    今天课程内容
                </h1>

                <ul>
                    {listArr}
                    <li>
                        <h3>这是标题</h3>
                        <p>内容</p>
                    </li>
                </ul>

                <h1>复杂没有用组件完成列表</h1>
                <ul>
                {
                    this.state.list.map((item,index)=>{
                        return (
                            <li key={index} onClick={(event)=>{this.clickFn(index,item.title,event)}}>
                                <h3>{index+1}-复杂-{item.title}</h3>
                                <p>{item.content}</p>
                            </li>
                        )
                    })
                }
                </ul>

            </div>
        )
    }

    clickFn=(index,title,event)=>{
        alert((index+1)+"-clickFn-"+title)
    }
}

ReactDOM.render(
    <Welcome></Welcome>,
    document.querySelector('#root')
)
  • 总结:上述代码实现了列表内容的渲染,先是定义了Welcome组件来实现state值的初始化和页面的总体布局的渲染;然后定义了列表组件(函数式和类式)实现列表的渲染;然后Welcome组件调用列表组件进行最终的渲染。

实践:react制作疫情地图

  • 首先是获取数据,爬数据小知识
//要先 npm install axios
//然后 node spider.js 来运行
let axios = require('axios');

(async function(){
    let httpUrl = 'https://c.m.163.com/ug/api/wuhan/app/index/feiyan-data-list?t='
    let res = await axios.get(httpUrl+new Date().getTime())
    console.log(res.data)
})()
 

这里获取的json数据格式如下

"data":{"list":[{"name":"酉阳县","province":"重庆","confirm":1,"suspect":null,"heal":null,"dead":null,"page":1},.......
  1. 定义数据对象:因为是对于每个省份的相关数据的操作,所以定义一个省份对象,包含data里面的数据类型。
  2. 取数据并加工然后进行forEach循环,内部用箭头函数中的括号值获取省份对象的对应位置的值,然后进行对象判空的初始化,不空就对对象进行数据操作。
  3. 定义到list数组,并插入数据 然后通过循环中的key挨个push json中的对象到list数组里,再进行sort排序(注意函数内部设的参数的值a
  4. 将处理好的list数组丢进来进行this.props.list.map((item,index)=〉{return()}的渲染
import React from 'react';
import ReactDOM from 'react-dom';
import jsonData from './feiyan.json'
import './06style.css'
/* './06style.css'

	ul,li{
	    list-style: none;
	    margin: 0;
	    padding: 5px 5px;
	}
	li{
	    display: flex;
	    
	}
	span{
	    flex: 1;
	}
	
	li:nth-child(odd){
	    background-color: #efefef;
	}
	
*/

console.log(jsonData)
//省份对象 统计相关数据
let provincesObj = {
    // "广东省":{
    //     confirm:0,
    //     suspect:0,
    //     heal:0,
    //     deal:0,
    // }
}
//对列表进行forEach循环
jsonData.data.list.forEach((item,i) => {
    //判断province有没有值
    if(provincesObj[item.province]==undefined){
        provincesObj[item.province] = {
            confirm:0,
            heal:0,
            dead:0,
        }
    }
    //判断item是不是none
    item.confirm = item.confirm?item.confirm:0;
    item.heal = item.heal?item.heal:0;
    item.dead = item.dead?item.dead:0;

    provincesObj[item.province] = {
        confirm:provincesObj[item.province].confirm+item.confirm,
        heal:provincesObj[item.province].heal+item.heal,
        dead:provincesObj[item.province].dead+item.dead
    }
});
//得到list
let provinceList = []
for (const key in provincesObj) {
    provincesObj[key].province = key;
    provinceList.push(provincesObj[key])
}


console.log(provincesObj)
console.log(provinceList)

//从大到小排序
let provinceListSort = provinceList.sort((a,b)=>{
    if(a.confirm<b.confirm){
        return 1;
    }else{
        return -1;
    }
})

console.log(provinceListSort)


class Bili  extends React.Component{
    constructor(props){
        super(props)
    }
    render(){
        return (
            <div>
                <h1>中国病例</h1>
                <ul>
                    <li>
                        <span>地区</span>
                        <span>确诊</span>
                        <span>死亡</span>
                        <span>治愈</span>
                    </li>

                    {
                        this.props.list.map((item,index)=>{
                            return (
                                <li>
                                    <span>{item.province}</span>
                                    <span>{item.confirm}</span>
                                    <span>{item.dead}</span>
                                    <span>{item.heal}</span>
                                </li> 
                            )
                        })


                    }
                </ul>
            </div>
        )
    }
}


ReactDOM.render(
    <Bili list={provinceListSort}></Bili>,
    document.querySelector('#root')
)

3.深入理解react

生命周期

生命周期即是组件从实例化到渲染到最终从页面中销毁,整个过程就是生命周期,在这生命周期中,我们有许多可以调用的事件,也俗称为钩子函数

生命周期的3个状态:

  • Mounting:将组件插入到DOM中
  • Updating:将数据更新到DOM中
  • Unmounting:将组件移除DOM中

生命周期中的钩子函数(方法,事件)

  • CompontWillMount :组件将要渲染,AJAX,添加动画前的类
  • CompontDidMount:组件渲染完毕,添加动画
  • compontWillReceiveProps:组件将要接受props数据,查看接收props的数据什么
  • ShouldComponentUpdate:组件接收到新的state或者props,判断是否更新。返回布尔值
  • CompontWillUpdate:组件将要更新
  • ComponentDidUpdate:组件已经更新
  • ComponentwillUnmount:组件将要卸载
import React,{Component} from 'react';
import ReactDOM from 'react-dom';




class ComLife extends Component{
    constructor(props){
        super(props)//调用继承Component的构造函数
        this.state = {
            msg:"hello world"
        }
        console.log("constructor构造函数")

    }
    componentWillMount(){
        console.log("componentWillMount组件将要渲染")
    }
    componentDidMount(){
        console.log("componentDidMount组件渲染完毕")
    }
    componentWillReceiveProps(){
        console.log("componentWillReceiveProps组件将要接收新的state和props")
    }
    shouldComponentUpdate(){
        //如果希望更新,就返回为真,不希望更新就返回为false
        console.log("进行判断是否要更新内容")
        console.log(this.state.msg)
        if(this.state.msg==="hello world"){
            console.log(true)
            return true
        }else{
            console.log(false)
            return false
        }
        
    }
    componentWillUpdate(){
        console.log('componentWillUpdate组件将要更新')
    }
    componentDidUpdate(){
        console.log('componentDidUpdate组件更新完毕')
    }
    componentWillUnmount(){
        console.log('componentWillUnmount移除')
    }
    render(){
        console.log('render渲染函数')
        return (
            <div>
                <h1>{this.state.msg}</h1>
                <button onClick={this.changeMsg}>组件更新</button>
            </div>
        )
    }
    changeMsg=()=>{
        this.setState({
            msg:"hello 老陈"
        })
    }
}

class ParentCom extends Component{
    constructor(props){
        super(props)
        this.state = {
            isShow:true
        }
    }
    render(){
        if(this.state.isShow){
            return (
                <div>
                    <button onClick={this.removeCom}>移除comlife</button>
                    <ComLife></ComLife>
                </div>
            )
        }else{
            return <h1>将comlife已移除</h1>
        }
    }
    removeCom=()=>{
        this.setState({
            isShow:false
        })
    }
}

ReactDOM.render(
    <ParentCom></ParentCom>,
    document.querySelector('#root')
)

上述代码逻辑:

  • 首先定义一个ParentCom类组件,进行原始页面的渲染,同时要为了验证移除和更新功能:

  • 移除:
    通过点击事件设置state中isShow的值,因为state改变了所以parent要重新渲染,然后就不用子组件ComLife了,子组件执行componentWillUnmount移除操作。

  • 更新操作是写到子组件里面的,点击事件进行changeMsg操作,
    进行更新前的虚拟DOM比较shouldComponentUpdate(),如果返回的true就然后进行更新,
    然后componentWillUpdate组件将要更新
    render渲染函数
    componentDidUpdate组件更新完毕

效果如下:

  • 页面渲染初始化时:
constructor构造函数
index.js:17 componentWillMount组件将要渲染
index.js:48 render渲染函数
componentDidMount组件渲染完毕
  • 点击移除事件:componentWillUnmount移除
  • 点击更新事件:
进行判断是否要更新内容
index.js:28 hello world
index.js:30 true
index.js:39 componentWillUpdate组件将要更新
index.js:48 render渲染函数
index.js:42 componentDidUpdate组件更新完毕

下面来看这段另外一个实现点击记次数的代码:
首先定义Button类组件,构建页面框架:带有onClick监听事件改变this.state值的Button和传state.data进行后面段落渲染的Content。然后就是生命周期的一些操作,详见上面的实现方式。

class Button extends React.Component {
  constructor(props) {
      super(props);
      this.state = {data: 0};
      this.setNewNumber = this.setNewNumber.bind(this);
  }
  
  setNewNumber() {
    this.setState({data: this.state.data + 1})
  }
  render() {
      return (
         <div>
            <button onClick = {this.setNewNumber}>INCREMENT</button>
            <Content myNumber = {this.state.data}></Content>
         </div>
      );
    }
}


class Content extends React.Component {
  componentWillMount() {
      console.log('Component WILL MOUNT!')
  }
  componentDidMount() {
       console.log('Component DID MOUNT!')
  }
  componentWillReceiveProps(newProps) {
        console.log('Component WILL RECEIVE PROPS!')
  }
  shouldComponentUpdate(newProps, newState) {
        return true;
  }
  componentWillUpdate(nextProps, nextState) {
        console.log('Component WILL UPDATE!');
  }
  componentDidUpdate(prevProps, prevState) {
        console.log('Component DID UPDATE!')
  }
  componentWillUnmount() {
         console.log('Component WILL UNMOUNT!')
  }
 
    render() {
      return (
        <div>
          <h3>{this.props.myNumber}</h3>
        </div>
      );
    }
}
ReactDOM.render(
   <div>
      <Button />
   </div>,
  document.getElementById('example')
);

表单

  • 以下实现了下拉列表框的表单操作
  • 代码很经典,通过select中的onChange监听事件进行setState操作,form的onSubmit监听事件进行提交相关操作的复写,并设置event.preventDefault();阻止浏览器的默认表单提交操作。
class FlavorForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: 'coconut'};
 
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }
 
  handleChange(event) {
    this.setState({value: event.target.value});
  }
 
  handleSubmit(event) {
    alert('Your favorite flavor is: ' + this.state.value);
    event.preventDefault();
  }
 
  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          选择您最喜欢的网站
          <select value={this.state.value} onChange={this.handleChange}>
            <option value="gg">Google</option>
            <option value="rn">Runoob</option>
            <option value="tb">Taobao</option>
            <option value="fb">Facebook</option>
          </select>
        </label>
        <input type="submit" value="提交" />
      </form>
    );
  }
}
 
ReactDOM.render(
  <FlavorForm />,
  document.getElementById('example')
);
  • 以下代码实现了多个表单的操作,通过对不同input表单的name赋值来区分,然后 handleInputChange(event){}来根据 event.target.name来进行setState操作。
class Reservation extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isGoing: true,
      numberOfGuests: 2
    };
 
    this.handleInputChange = this.handleInputChange.bind(this);
  }
 
  handleInputChange(event) {
    const target = event.target;
    const value = target.type === 'checkbox' ? target.checked : target.value;
    const name = target.name;
 
    this.setState({
      [name]: value
    });
  }
 
  render() {
    return (
      <form>
        <label>
          是否离开:
          <input
            name="isGoing"
            type="checkbox"
            checked={this.state.isGoing}
            onChange={this.handleInputChange} />
        </label>
        <br />
        <label>
          访客数:
          <input
            name="numberOfGuests"
            type="number"
            value={this.state.numberOfGuests}
            onChange={this.handleInputChange} />
        </label>
      </form>
    );
  }
 }

案例:配合ajax获取的数据的疫情进展显示

效果如下:
【React】看完这篇文章能够学会React初级技术_第5张图片
疫情进展的实现:

import React from 'react';
import axios from 'axios';
import bannerImg from '../assets/img/banner.jpg'

class NewsCom extends React.Component{
    constructor(props){
        super(props)
        this.state = {
            datalist:[]
        }
    }
    async componentWillMount(){
        let res = await axios.get('http://localhost:8080/api/news');
        //console.log(res.data)
        let data = JSON.parse(res.data.data[0].content) 
        console.log(data.sub_raw_datas)
        this.setState({
            datalist:data.sub_raw_datas
        })
    }
    render(){
        console.log(this.state.datalist);
        return (
            
            <div className="contentItem new">
                <div className="banner">
                    <img alt="banner" src={bannerImg} />
                    <h1>疫情追踪</h1>
                </div>

                <div className="newContent">
                    <div className="line"></div>
                    <div className="newsList">
                        {
                            
                            this.state.datalist.map((item,index)=>{

                                if(item.raw_data.event_image){
                                    return (
                                        <div className="newsListItem" key={index}>
                                            <div className="time">{item.raw_data.showtime_string}</div>
                                            <div className="desc">
                                                {item.raw_data.desc}
                                            </div>
                                            <div className="img">
                                                <img src={item.raw_data.event_image.url} alt="img" />
                                            </div>
                                        </div>
                                    )
                                }else{
                                    return (
                                        <div className="newsListItem" key={index}>
                                            <div className="time">{item.raw_data.showtime_string}</div>
                                            <div className="desc">
                                                {item.raw_data.desc}
                                            </div>
                                            
                                        </div>
                                    )
                                }
                                
                            })
                        }
                    </div>
                </div>
            </div>
        )
    }

    
}

export default NewsCom;

server是这样的:

const express = require('express');
const axios = require('axios')
const app = express()



app.get('/',(req,res)=>{
    res.send("返回抗疫数据的api服务器")
})

app.get('/api/newsdata',async (req,res)=>{
    //解决ajax跨域问题
    res.append("Access-Control-Allow-Origin","*")
    res.append("Access-Control-Allow-content-type","*")
    //请求头条数据
    let result = await axios.get('https://i.snssdk.com/forum/home/v1/info/?activeWidget=1&forum_id=1656784762444839')
    let data = result.data;
    
    res.send(data)
})


app.get('/api/news',async (req,res)=>{
    //解决ajax跨域问题
    res.append("Access-Control-Allow-Origin","*")
    res.append("Access-Control-Allow-content-type","*")
    //请求头条数据
    let httpUrl = 'https://i.snssdk.com/api/feed/forum_flow/v1/?activeWidget=1&query_id=1656810113086509&tab_id=1656810113086525&category=forum_flow_subject&is_preview=0&stream_api_version=82&aid=13&offset=0&count=20'
    let result = await axios.get(httpUrl)
    let data = result.data;
    
    res.send(data)
})


app.listen(8080,()=>{
    console.log("server Start:")
    console.log("http://localhost:8080")
    console.log("http://localhost:8080/api/newsdata")
})

4.插槽、路由

react插槽

  • 插槽:组件中写入内容,这些内容可以被识别和控制。
  • react插槽原理:组件中写入的HTML,可以传入到props中。

组件中的HTML内容直接全部插入

class ParentCom extends React.Component{
    
    render(){
        console.log(this.props)
        return (
            <div>
                <h1>组件插槽</h1>
                {this.props.children}
            </div>
        )
    }
}

组件中根据HTML内容的不同,插入的位置不同。
以下实现了:父组件在子组件的创建中设置子组件的props,子组件可以根据props来进行相关运算。

import React from 'react';
import ReactDOM from 'react-dom';

class ParentCom extends React.Component{
    
    render(){
        console.log(this.props)
        return (
            <div>
                <h1>组件插槽</h1>
                {this.props.children}
                <ChildCom>
                    <h1 data-position="header">这是放置到头部的内容</h1>
                    <h1 data-position="main">这是放置到主要的内容</h1>
                    <h1 data-position="footer">这是放置到尾部的内容</h1>
                </ChildCom>
            </div>
        )
    }
}

class ChildCom extends React.Component{
    render(){
        let headerCom,mainCom,footerCom;
        this.props.children.forEach((item,index)=>{
           if(item.props['data-position']==='header'){
            headerCom = item
           }else if(item.props['data-position']==='main'){
               mainCom = item
           }else{
               footerCom = item
           }
        })
        return (
            <div>
                <div className="header">
                    {headerCom}
                </div>
                <div className="main">
                    {mainCom}
                </div>
                <div className="footer">
                    {footerCom}
                </div>
            </div>
        )
    }
}

class RootCom extends React.Component{
    constructor(props){
        super(props)
        //console.log(props)
        this.state = {
            arr:[1,2,3]
        }
    }
    render(){
        return (
            <ParentCom>
                <h2 data-name="a" data-index={this.state.arr[0]}>子组件1</h2>
                <h2 data-name="b" data-index={this.state.arr[1]}>子组件2</h2>
                <h2 data-name="c" data-index={this.state.arr[2]}>子组件3</h2>
            </ParentCom>
        )
    }
}

ReactDOM.render(
    <RootCom></RootCom>
    ,
    document.querySelector("#root")
)

react路由

  • 根据不同的路径,显示不同的内容

安装:

npm install react-router-dom -save
  • Router:所有路由组件的根组件(底层组件),包裹路由规则的最外层容器。
    属性:basename->设置跟此路由根路径,router可以在1个组件中写多个。
  • Route:路由规则匹配组件,显示当前规则对应的组件
  • Link:路由跳转的组件

以下为路由案例

  • 下面是很直接的方式
import React from 'react';
//hash模式
//import {HashRouter as Router,Link,Route} from 'react-router-dom'

//history模式/后端匹配使用
import {BrowserRouter as Router,Link,Route} from 'react-router-dom'

function Home(){
    return (
        <div>
            <h1>admini首页</h1>
        </div>
    )
}

function Me(){
    return (
        <div>
            <h1>admin个人中心</h1>
        </div>
    )
}

function Product(){
    return (
        <div>
            <h1>admin产品页面</h1>
        </div>
    )
}

class App extends React.Component{
    
    render(){
        return (
            <div id="app">
                {/* 
所有页面普通内容
*/
} <Router> <Route path="/" exact component={()=>(<div>首页</div>)}></Route> <Route path="/me" component={()=>(<div>me</div>)}></Route> <Route path="/product" component={()=>(<div>product</div>)}></Route> </Router> <Router> {/*
Home Product 个人中心
*/
} <Route path="/admin/" exact component={Home}></Route> <Route path="/admin/product" component={Product}></Route> <Route path="/admin/me" exact component={Me}></Route> </Router> </div> ) } } export default App
  • Route中的exact:

一般而言,react路由会匹配所有匹配到的路由组件,exact能够使得路由的匹配更严格一些。exact的值为bool型,为true是表示严格匹配,要求路径与location.pathname必须完全匹配,且会比较匹配过的路由是否和正匹配的路由一致,为false时为正常匹配。

如在exact为true时,’/link’与’/’是不匹配的,但是在false的情况下它们又是匹配的。

<Route path='/' component={App} />
<Route path='/Home' component={Home} />
<Route path='/About' component={About} />
 
​//这种情况下,如果点击Home,匹配路由path='/Home',那么会把App也会展示出来。
 
<Route exact path='/' component={App} />
<Route path='/Home' component={Home} />
<Route path='/About' component={About} />

//这种情况,如果点击Home,匹配路由path='/Home',那么App就不会展示出来
  • 下面是需要在URL上的一个操作
class App extends React.Component{
    
    render(){
        let meObj = {
            pathname:"/me",//跳转的路径
            search:"?username=admin",//get请求参数
            hash:"#abc",//设置的HASH值
            state:{msg:'helloworld'}//传入组件的数据
        };
        return (
            <div id="app">
                <Router>
                    <div className="nav">
                        <Link to="/">Home</Link>
                        <Link to="/product">Product</Link>
                        <Link to={ meObj }   replace>个人中心</Link>
                        <Link to="/news/4568789">新闻页</Link>
                    </div>
                    <Route path="/" exact component={Home}></Route>
                    <Route path="/product" component={Product}></Route>
                    <Route path="/me" exact component={Me}></Route>
                    <Route path="/news/:id" component={News}></Route>
                </Router>
            </div>
        )
    }
}
  • switch
    例如以下代码实现了:
    如果URL是/about, 将会开始寻找相匹配的。将会被匹配到,紧接着 会停止继续匹配并且渲染。 【React】看完这篇文章能够学会React初级技术_第6张图片
import React from 'react';
import {BrowserRouter as Router,Route,Link,Redirect,Switch} from 'react-router-dom'


function LoginInfo(props){
    //props.loginState = 'success';
    //props.loginState = "fail"
    console.log(props)
    if(props.location.state.loginState === 'success'){
        return <Redirect to="/admin"></Redirect>
    }else{
        return <Redirect to="/login"></Redirect>
    }
}

let FormCom = ()=>{
    let pathObj = {
        pathname:"/logininfo",
        state:{
            loginState:'success'
        }
    }
    return (
        <div>
            <h1>表单验证</h1>
            <Link to={pathObj}>登录验证后页面</Link>
        </div>
    )
}


class ChildCom extends React.Component{
    render(){
        return (
        <div>
            <button onClick={this.clickEvent}>跳转到首页</button>
        </div>
        )
    }
    clickEvent=()=>{
        //console.log(this.props)
        //this.props.history.push("/",{msg:"这是由ChildCom组件发给首页的数据"})
        //this.props.history.replace("/",{msg:"这是由ChildCom组件发给首页的数据"})
        //前进
        //this.props.history.go(1)
        //this.props.history.goForward()
        //后退
        //this.props.history.go(-1)
        //this.props.history.goBack()
    }
}

class App extends React.Component{
    render(){
        return (
            <div>
                <Router>
                    <Switch>
                        <Route path="/:id" exact  component={(props)=>{console.log(props);return (<h1>首页</h1>)}}></Route>
                        <Route path="/form" exact  component={FormCom}></Route>
                        <Route path="/login" exact  component={()=>(<h1>登录页</h1>)}></Route>
                        <Route path="/logininfo" exact component={LoginInfo}></Route>
                        <Route path="/admin" exact component={()=>(<h1>admin页,登录成功</h1>)}></Route>
                        <Route path="/abc" exact component={()=>(<h1>abc1页,登录成功</h1>)}></Route>
                        <Route path="/abc" exact component={()=>(<h1>abc2页,登录成功</h1>)}></Route>
                        <Route path="/child" component={ChildCom} ></Route>
                    </Switch>
                </Router>
            </div>
        )
    }
}

export default App

解构

JS中的解构

  • react中的解构
    使用了lodash/find,重新赋值了一个新的数组,但是原数组依旧被更改了,为什么?
import find from 'lodash/find';
state={
	data:[{
		id:1,
		name:'xiaobe'
	},{
		id:2,
		name:'Sam'
	}]
}
fn(){
	const{data}=this.state;
	constnewData=[...data];
	console.log('修改前data',data);
	constnewItem=find(newData,{id:1})||{};
	console.log('修改前newItem',newItem);
	if(Object.keys(newItem).length){
		newItem.name='new-xiaobe';
	}
	console.log('修改后newItem',newItem);
	this.setState(
	{
		data:[...newData],
	},
	()=>{
		console.log('修改后data',data);
	},);
}

发现,修改前data里第一条name已经从xiaobe变成了new-xiaobe
[…data]到底干了啥?下面以你提供的数据来解释以下:
为了方便理解,我改造一下data。

const a={
	id:1,
	name:'xiaobe'
}
const b={
	id:2,
	name:'Sam'
}
const data=[a,b]

[…data]的流程如下:

const newData=[]//newArray()
newData.push(a)
newData.push(b)

通过上面的代码,我们发现如果直接判断newData===data,结果一定为false,因为newData的内存地址和data的不一致。
但是如果你判断newData[0]===data[0]以及newData[1]===data[1],那结果变为true,子项的内存地址并没有发生变化。
当你修改第一项的name值之后,便会影响到data和newData,因为这个子项被data和newData都引用了。

5.Redux

使用原因和设计理念

  • 在React中,数据在组件中是单向流动的,数据从一个方向父组件流向子组件(通过props),所以,两个非父子组件之间通信就相对麻烦,redux的出现就是为了解决state里面的数据问题
  • Redux是将整个应用状态存储到一个地方上称为store,里面保存着一个状态树store tree,组件可以派发(dispatch)行为(action)给store,而不是直接通知其他组件,组件内部通过订阅store中的状态state来刷新自己的视图

【React】看完这篇文章能够学会React初级技术_第7张图片

三大原则

  1. 唯一数据源
    整个应用的state都被存储到一个状态树里面,并且这个状态树,只存在于唯一的store中
  2. 保持只读状态
    state是只读的,唯一改变state的方法就是触发actionaction是一个用于描述以发生事件的普通对象
  3. 数据改变只能通过纯函数来执行
    使用纯函数来执行修改,为了描述action如何改变state的,你需要编写reducers

概念解析

  1. Store
    store就是保存数据的地方,你可以把它看成一个数据,整个应用智能有一个store
    Redux提供createStore这个函数,用来生成Store
import {createStore} from 'redux'
const store=createStore(fn);
  1. State
    state就是store里面存储的数据,store里面可以拥有多个state,Redux规定一个state对应一个View,只要state相同,view就是一样的,反过来也是一样的,可以通过store.getState( )获取
import {createStore} from 'redux'
const store=createStore(fn);
const state=store.getState()
  1. Action
    state的改变会导致View的变化,但是在redux中不能直接操作state也就是说不能使用this.setState来操作,用户只能接触到View。在Redux中提供了一个对象来告诉Store需要改变state。Action是一个对象其中type属性是必须的,表示Action的名称,其他的可以根据需求自由设置。
const action={
  type:'ADD_TODO',
  payload:'redux原理'
}

在上面代码中,Action的名称是ADD_TODO,携带的数据是字符串‘redux原理’,Action描述当前发生的事情,这是改变state的唯一的方式

  1. store.dispatch( )
    store.dispatch( )是view发出Action的唯一办法
store.dispatch({
  type:'ADD_TODO',
  payload:'redux原理'
})

store.dispatch接收一个Action作为参数,将它发送给store通知store来改变state。

  1. Reducer
    Store收到Action以后,必须给出一个新的state,这样view才会发生变化。这种state的计算过程就叫做Reducer。
    Reducer是一个纯函数,他接收Action和当前state作为参数,返回一个新的state
    注意:Reducer必须是一个纯函数,也就是说函数返回的结果必须由参数state和action决定,而且不产生任何副作用也不能修改state和action对象
```javascript
const reducer =(state,action)=>{
  switch(action.type){
    case ADD_TODO:
        return newstate;
    default return state
  }
}
  1. Provider组件
    将store的state数据和要改变数据的组件进行关联
  2. connct方法
    对组件和数据进行连接

步骤:

  1. 创建reduce,初始化数据以及初始化相关数据改变的动作,实例出数据仓库
  2. 数据的获取修改,两个映射函数,一个将state数据映射到props里,一个将修改数据的方法映射到组件的props中。
  3. 然后进行这两个映射函数的数据connect关联
import React from 'react';
import ReactDOM from 'react-dom';
import {createStore} from 'redux'

//用于通过动作,创建新的state
const reducer = function(state={num:0},action){
    console.log(action)
    switch(action.type){
        case "add":
            state.num++;
            break;
        case 'decrement':
            state.num--;
            break;
        default:
            break;
    }
    return {...state}//相当于对象的COPY
}


//创建仓库
const store = createStore(reducer)
console.log(store)


function add(){
    //通过仓库的方法dispatch进行修改数据
    store.dispatch({type:"add",content:{id:1,msg:"helloworld"}})
    console.log(store.getState())
}

function decrement(){
    //通过仓库的方法dispatch进行修改数据
    store.dispatch({type:"decrement"})
    console.log(store.getState())

}

//函数式计数器
const Counter = function(props){
    //console.log(store.getState())
    let state = store.getState()
    return (
        <div>
            <h1>计数数量:{state.num}</h1>

            <button onClick={add}>计数+1</button>
            <button onClick={decrement}>计数-1</button>
        </div>
    )

}



ReactDOM.render(<Counter></Counter>,document.querySelector("#root"))

store.subscribe(()=>{
    ReactDOM.render(<Counter></Counter>,document.querySelector("#root"))
})

下面看一个可以+1和+5的加法器

import React from 'react';
import ReactDom from 'react-dom'
import {createStore} from 'redux'
//Provide 跟store 和视图结合起来
//connect 数据的连接
import {Provider,connect} from 'react-redux'

class Counter extends React.Component{
    render(){
        //用于计数,通过组件来获取值,组件的props通过store的state来获取的
        const value = this.props.value;
        //添加事件,将修改事件的数据或者方法传入到props
        const onAddClick = this.props.onAddClick;
        return (
            <div>
                <h1>计数的数量:{value}</h1>
                <button onClick = {onAddClick}>数字+1</button>
                <button onClick = {this.props.onAddClick5}>一次性+5</button>
            </div>
        )
    }
}
const addAction = {
    type : 'add'
}
//不要让reducer放很多方法 所以在外面定义一个action的对象
ActionFnObj = {
    add:function(state,action){
        return state;
    },
    addNum:function(state,action){
        state.num = state.num + action.num;
        return state;
    }
}
//设置reducer默认值
function reducer(state = {num : 0},action){
    //判断action的字段中有无redux,没有就是要进行初始化的意思
    if(action.type.indexOf('redux')==-1){
        state = ActionFnObj[action.type](state,action);
        return {...state};
    } else{
        return state;
    }

    //在action对象中找到相关方法 然后设置action.asg的参数
    //这样写就可以不用switch了
    
    // switch(action.type){
    //     case 'add':
    //         state.num++;
    //         break;
    //     default:
    //         break;
    // }
    //解构挨个返回新的state
    return {...state}
}
//创建一个store
const store = createStore(reducer)
//将store的state数据映射到props
//将状态映射到props的函数
function mapStateToProps(){
    //返回props 将state的num传给props的value
    return {
        value:state.num
    }
}
//将state的dispatch映射到组件的props里面 实现了方法共享
//修改state数据的方法 映射到props 默认会传入store里面的dispatch方法
function mapDispatchToprops(){
    return {
        //放一个props方法 匿名函数或者是箭头函数
        onAddClick:(dispatch)=>{
            dispatch(addAction);
        },
        onAddClick5:(dispatch)=>{
            dispatch({type:'addNum',num:5});
        }
    }
}
//将上面的这两个数据仓库的state和修改state的方法关联映射到Counter组件上,形成新的组件
const App = connect(
    mapStateToProps,
    mapDispatchToprops
)(Counter)
//更新渲染
//Provider 将store的数据传进去 并且做数据的更新
ReactDOM.render(
    <Provider store = {store}>
        <App></App>
    </Provider>,
    document.querySelector('#root')
)

的解释如下:

  1. 根据onAddClick5调用到(传入store方法类型数据的方法)
  2. 然后根据映射store里的方法的方法,触发reduce的执行
  3. 然后触发对reduce内部设置的操作,再通过传进reduce的值进行调用,返回新的state

你可能感兴趣的:(#,【React】,「,前端,」专栏,React)