React

Posted 煜成'Studio

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React相关的知识,希望对你有一定的参考价值。

React

2013年Facebook推出的开源的函数式编程的框架,只支持IE8及以上。

16版本之后成为React Fiber(React16版本或者说是16版本中的一些底层事件),react底层在事件循环中加入了优先级的概念,可以利用事件循环的一些碎片时间执行一些高优先级的用户交互,提高react.js使用过程中的用户体验

与Vue.js的比较:react.js灵活性更大一些,所以处理非常复杂的业务时,技术方案有更多的选择,用于复杂度比较高的项目;vue.js提供了更多的API,实现功能更简单,但是API多,所以灵活性就有一定的限制了,多用于面向用户端不太复杂的项目,当然也可以做一些大型复杂的项目

安装

//安装脚手架工具create-react-app
npm i -g create-react-app

//创建项目
create-react-app 项目名字
(
报错:
创建react项目的时候node版本太低,用nvm添加了新版本之后,执行命令create-react-app myproject报错
'create-react-app' 不是内部或外部命令,也不是可运行的程序或批处理文件。
解决方法:
可以使用npx create-react-app myproject命令(这是官网上新版本的命令),
如果还是报错,是关于npx的错误,把当前使用的node版本的路径D:\\nvm\\nvm-setup\\installation\\nvm\\v16.13.2加到环境变量中去,
其他错误的话,再执行一次npx create-react-app myproject命令,
我是第一次没成功,第二次又执行一次才成功的
)

//启动项目
yarn start 或者 npm run start
//在localhost:3000地址展示,借助webpack-dev-server开启的地址

//开发完项目,最终打包
yarn build

yarn test //不用

yarn eject
//项目自动把有关webpack相关的文件都隐藏了,怕你自己修改后webpack崩了。执行这个语句,会把webpack相关文件都展示出来,例如webpack.config.js。一旦执行了这个命令,就不能再返回到隐藏状态了。

附:工程化:如果在项目中用到了像webpack这样全自动化的构建工具,你写了一段代码,它能帮你进行语法检查、代码压缩、语法转换、兼容性处理等等一系列的自动的东西

附:浏览器,按住shift点击刷新就是强制刷新

工程目录文件简介

git文件夹

git仓库

yarn.lock

yarn的一些缓存文件,能让你在下一次下载这些包的时候速度更快,其中也有项目依赖安装包的一些版本号,不要改动

README.md

项目说明文件,markdown写的,可以自己修改

package.json

代表这个脚手架工具是一个node的包文件,其实是node里的一些内容,比如项目的介绍、依赖于的第三方的包、调用的指令,可以把项目变成一个node的包

.gitignore

用git管理代码时,有一些文件不想传到git仓库上,把这些文件定义在这个文件里面

node_modules

项目依赖的第三方的包/模块,不要改动

public文件夹下————————————————

favicon.ico

网站最上面的小图标

index.html

是项目的html主文件,项目首页的html模板。

link引入favicon.ico,%PUBLIC_URL%是React脚手架的关键词写法,就代表public这个文件夹的路径,ref内部也可以用相对路径的形式,%PUBLIC_URL%也有一定的优势,在使用路由的时候能感受到

<link rel="icon" ref="%PUBLIC_URL%/favicon.ico" />

meta标签,name=“viewport”,开启理想视口,用于做移动端网页的适配

meta标签,name=“theme-color”,用于配置浏览器页签(favicon.ico图标那个位置)和地址栏的颜色,仅支持安卓手机浏览器。

meta标签,name=“description”,描述网站信息的,利于SEO

link标签,ref=“apple-touch-icon”,将手机浏览器的网页添加到手机桌面时(向一个APP一样可以点击进入网页),在桌面上展示的图标,只支持苹果手机

<link rel="apple-touch-icon" ref="%PUBLIC_URL%/logo193.png" />

link标签,rel=“manifest”,引入应用加壳时的配置文件(在html网页的套一个安卓的壳,就变成了安卓手机上的应用,生成一个.apk;套一个ios的壳,就变成了苹果手机应用),在下面的manifest.json和src/index.js部分也有讲解。

<link rel="manifest" ref="%PUBLIC_URL%/manifest.json" />

noscript标签作用是如果llq(浏览器)把script禁掉了,不支持js脚本的运行,给用户提示这个标签内的内容,用于容错的标签;

id为root的div标签是程序的主入口。

robots.txt

爬虫规则文件,什么可以被爬,什么不可以

manifest.json

配置加壳应用在手机上的权限,应用的图标等。(结合src文件夹下的index.js文件里的registerServiceWorker)如果网页可以当一个app来使用,就可以把它存储在桌面上,有一个快捷方式,可以直接进入这个网址。

src文件夹下————————————————

放的是项目的所有的源代码

index.js

是所有代码的主入口文件(webpack如果想使用,需要一个入口文件),整个程序从index.js里逐行执行,所有组件都从这里挂载。引入中没写文件名后缀,会优先寻找引入目录下的js文件。

里面引入了reactreact-dom(处理React和DOM之间的相关事宜的)等第三方模块(在package.json可以看到已经安装的第三方的包)。

还引入了.css文件(react一个非常重要的设计理念: all in js,文件都可以像模块一样引入,vue、angular也是)。

引入组件,例如<App />,组件的文件是App.js

还引入了registerServiceWorker,一个概念叫PWA(progressive web app),通过写网页的形式写一些手机的app应用。registerServiceWorker帮助我们借助网页写手机app,加入引用了它,写了一个网页并上线到一台支持https的服务器上,我们写的网页就有这样的特性了,用户第一次连接网页需要联网,但是突然断网了,此时用户第二次访问页面,依然可以看到之前访问的页面,registerServiceWorker会把之前的网页存储在我们浏览器内,有缓存,下一次即使没有网络也可以把该网页当成一个app来使用。

自己创建的组件需要挂载到DOM节点下,ReactDOM是第三方模块,使用**ReactDOM.render()**方法就可以把我们自己写好的组件挂载到DOM节点下,第一个参数是引入的组件,第二个参数是DOM标签,第三个参数可以是registerServiceWorker(),可选。

index.js(官网最新版,和上面的文件介绍稍微有些出入)

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

ReactDOM.render(
  <React.StrictMode> //不是ES5中的严格模式,而是检查App组件和其子组件的内容是否合理
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

index.css 放一些页面通用样式,例如body …

logo.svg 是一个图片

reportWebVitals.js 用于记录页面上的性能的,里面用web-vitals这个库实现页面性能的检测

setupTest.js 用于做组件测试的(应用的整体测试或者一个一个模块拼在一起时一个一个的单元测试),用的是jest-dom库

App.js

App组件。项目中自己写的组件通常放在src下的components文件夹下。

App.js

//组件必须引入Component这个基类并继承它,引入用到了JSX语法,所以也要引入react
import React from 'react';

class App extends React.Component 
    render()  
    //组件文件中,render()方法内部是该组件最终渲染的结果,render函数return什么就展示什么内容
        return (
            <div>
                <h1>hello</h1>
                <p>111</p>
            </div> 
        )
    


export default App

//另一种形式
import React,  Component  from 'react'; //使用ES6的解构赋值

class App extends Component 
    ...

App.test.js

自动化测试文件,因为做React或者Vue项目的时候,因为会涉及到一些函数式编程,所以会做一些自动化测试。

顺序是:index.js执行到需要root节点这里,去public文件夹下找index.html中的root节点(react中的webpack配置将index.js和index.html进行关联),于是App组件就渲染到页面上了,组件里面引入了样式也就生效了。

基础知识

函数式组件

<!--script标签分别引入react核心库、react-dom、babel(用于将JSX转为JS的)-->

<script type="text/babel">
    //创建函数式组件
    function MyComponent() 
        console.log(this); //undefined,因为babel编译后开启了严格模式
    	//严格模式禁止this指向window,this值为undefined
        return <h2>函数定义的组件,适用于【简单组件(没有状态state)】的定义</h2>
    
    //渲染组件到页面
    ReactDom.render(<MyComponent />, document.getElementById('test'))
    //ReactDom.render(<MyComponent />, document.getElementById('test'))之后发生了什么?
    //1.React解析组件标签,找到了MyComponent组件。
    //2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中
</script>

附:

<script>
    class Person 
        constructor(name, age) 
            //类的构造器中的this是指类的实例对象
            this.name = name;
            this.age = age;
        

        //类中定义的方法都是放在类的原型对象上,供实例去使用
        speak() 
            //speak方法放在类的原型对象上,供实例使用
            //通过Person实例调用speak时,speak中的this就是Person实例
            console.log(`我叫$this.name,我的年龄是$this.age`)
        
    

    const p1 = new Person('tom', 12)
    console.log(p1) //得到Person...,这里Person是斜体,表示是类Person的实例对象
    p1.speak()

    //继承
    class Student extends Person 
        constructor(name, age, grade) 
            super(name, age) //相当于调用了父类的constructor
            this.grade = grade
        
        //重写父类继承的方法
        speak() 
            ...
        
        //自己的方法
        study() 
            ...
        
    
</script>

类式组件

<!--script标签分别引入react核心库、react-dom、babel(用于将JSX转为JS的)-->
<script type="text/babel">
    //创建类式组件
    class MyComponent extends React.Component 
        render() 
            //render函数放在MyComponent的原型对象上,供实例使用
            //render中的this指的是MyComponent的实例对象,或者叫MyComponent组件实例对象
            return <h2>类定义的组件,适用于【复杂组件(有状态state)】的定义</h2>
        
    
    //渲染组件到页面
    ReactDOM.render(<MyComponent />, document.getElementById('test'))
	//ReactDom.render(<MyComponent />, document.getElementById('test'))之后发生了什么?
    //1.React解析组件标签,找到了MyComponent组件。
    //2.发现组件是使用函数定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法
    //3.将render返回的虚拟DOM转为真实DOM,随后呈现在页面中
</script>

JSX语法

javascript的语法扩展,在js文件中使用了标签的形式:使用自己创建的组件的自定义标签、render函数return内使用的自定义标签或者html标签等。

自定义标签第一个字母必须大写,区别于原始的html标签,如果小写的话,JSX语法是不支持的

返回的内容必须包含在一个大的元素内部,当然这个标签也会显示在html文档中,如果不想增加过多的标签,可以使用React提供的Fragment占位符,把需要return的内容都放在<Fragment>…</Fragment>里

import React,  Component, Fragment  from 'react'; //这里是export和export default一起引入的样式,不是React的解构赋值写法

class App extends Component 
    render()  
        return (
            <Fragment> //占位符
                <h1>hello</h1>
                <p>111</p>
            </Fragment> 
            //其实可以用空标签也能起效果,例如下面,但是Fragment标签是可以写key属性用于遍历,而空标签不可以添加任何属性。key这个标签属性是唯一的,因为Fragment最终会丢失掉,所以传其他属性没有意义,Fragment也不接收。
            //<>
            //    <h1>hello</h1>
            //</>
        )
    


export default App


//也可直接创建并暴露App组件
export default class App extends Component 
    render()  
        return (
            <Fragment> //占位符
                <h1>hello</h1>
                <p>111</p>
            </Fragment> 
        )
    

如果在文件中使用了JSX的语法,一定要引入react,import React from ‘react’,不引入是没办法编译JSX语法的。

在JSX模板中循环返回一个内容的时候,返回的内容不止是一条的话(注释也算),也必须有一个最外层的包裹元素

<ul>
	
        this.state.list.map((item, index) => 
            return (
                <div>
                    <TodoItem />
                    /*
                        注释内容
                    */
                </div>
            )
        )
    
</ul>

如果在JSX中使用js表达式或者js变量,形式是**JavaScript表达式Expressions**

附:js语句与js表达式

1.表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方,例如这些:

(1) a

(2) a + b

(3) demo(1)

(4) arr.map()

(5) function test()

2.语句,例如这些

(1) if()

(2) for()

(3) switch() case: …

在JSX里写注释

多行注释
\\*...\\*
单行注释,不能把这三行写在一行,因为写在一行会把后面的也认为是注释的一部分

//...

className在react中因为class这个关键词已经用于定义组件了,所以在元素或者组件上定义样式用className

<input className="input" />

不转义设置页面的内容,比如在input框内填写<h1>haha</h1>,想要输出就是一个大大的标题,而不是转义过后的带有标签的原样输出<h1>haha</h1>,可以用dangerouslySetInnerHTML,但这样会有XSS攻击的风险,但有时必须需要这样做

<li
    key=index
    onClick=this.handleItemDelete.bind(this, index)
    dangerouslySetInnerHTML = __html : item
    //外层花括号是JSX语法的花括号,表示里面需要一个js表达式,内层的花括号表示这个js表达式就是一个js对象
    //item为不转义显示在页面的内容
>
	//item,上面已经有item了,这里之前的item就没必要写了
</li>

附:

<div style=marginTop: '10px', marginLeft: '10px'>
//在标签上加样式用双,最外层为JSX语法,里面为样式的那个对象形式

label标签的作用是扩大点击的区域,如果想点击label标签光标自动聚焦到input框,可以用htmlFor

<label for="insertArea">输入内容</label>
<input id="insertArea" />
//这样会报警告,因为react中for认为是循环的那个for,所以如果label标签上要改为htmlFor
<label htmlFor="insertArea">输入内容</label>

响应式设计思想,react英文意思就是响应、反应

不要操作DOM,操作数据,React会感知数据的变化,自动的生成DOM

State(状态):存储组件的数据

组件内部的数据可以动态修改

this.setState()函数是更新state数据的唯一途径。react中有个immutable概念,是说state不允许我们做任何的改变,不要直接改state里面的内容,否则后面做性能优化的时候就会有问题,必须先拷贝一份,改拷贝里的内容

附:展开运算符复制数据和添加数据[…this.state.list, this.state.inputValue];splice用法

注意点:JavaScript表达式Expressions;事件绑定的时候需要对函数的作用域进行变更this.increaseLikes = this.increaseLikes.bind(this)或者onClick=() => this.increaseLikes() 还有带参数的onClick=this.handleItemDelete.bind(this, index)

import react from "react";

class LikesButton extends React.Component 
    //构造器是否接收props,是否super传递props,取决于:是否希望在构造器中通过实例this访问props。如果不接收props也不传递props,构造器中使用this.props则为undefined
    constructor(props) 
        super(props)//调用一次父类的构造函数
        this.state = 
            likes: 0,
            list: ['li', 'xin']
        
        // this.increaseLikes = this.increaseLikes.bind(this)
        //这句和下面第二种方式二选一,是确定this指向LikeButton组件的,否则this为undefined
        //其实这句是提取出来了,实际在onClick事件那里写也行,如下handleItemDelete.bind那里
        //通过this.事件名 = this.事件名.bind(this),写在constructor中,这样写会节约性能
        //分析:右边this是LikesButton的实例对象,引用原型对象上的increaseLikes方法,bind做了两件事,一个是生成新的函数,二是改了函数里的this,改成什么看传了什么,这里传了的是LikesButton的实例对象,所以右边返回一个函数,函数的this指的是LikesButton的实例对象。然后把这个函数放到了左侧this实例的自身,给函数起了一个名字叫increaseLikes。所以下面使用onClick=this.increaseLikes的时候相当于使用的实例对象自身多增加的increaseLikes方法,并不会再去找原型对象上的increaseLikes方法。紧接着下面有附例子
    

    increaseLikes() 
        //this.setState(
        //    likes: ++this.state.likes //使用数据
        //)
        //方法有参数e,是event对象,e.target就是该方法所在的DOM节点,
        //如果标签是input的话,e.target.value可以获得input框的value值
        //新版react的setState是一个函数的形式,返回一个对象
        this.setState(() => 
            return 
                likes: ++this.state.likes //以对象的形式返回
            
        )
        //es6函数直接简化为这样
        //this.setState(() => (
        //    likes: ++this.state.likes
        //)
        //setState更新状态时是一个异步为setState,为了性能的提升,但是异步的话,假如像这样使用e.target.value就会出现问题
        ///
        //handleInputChange(e) 
        //    this.setState(() => 
        //        return 
        //            inputValue: e.target.value
        //        
        //	)
        //
        //要把e.target.value提出来
        //handleInputChange(e) 
        //	  const value = e.target.value
        //    this.setState(() => 
        //        return 
        //            inputValue: value
        //        
        //	)
        //
        //this.setState(prevState) //其实setState有参数prevState,是修改数据之前的数据,等价于list:[...this.state.list, this.state.like]中的this.state,所以直接换为list:[...prevState.list, prevState.like]就行,这样写更靠谱,避免我们不小心改变了state的状态
        //判断setState()更新状态时异步还是同步的,主要是看执行setState的位置,
        //在React控制的回调函数中(生命周期钩子,react事件监听回调)这种情况是异步的;
        //在非react控制的异步回调函数中(定时器回调/原生事件监听回调/promise回调)这种情况是同步的。
    
    
    handleItemDelete(index) 
        console.log(index)
    
    
    render() 
        return (
            <Fragment>
                <p>this.state.likes</p>
                <button
                    type="button"
                    //onClick=this.increaseLikes //第一种方式,需要结合上面的this.increaseLikes = this.increaseLikes.bind(this)改变this指向,或者直接按照下面这么写
                    //onClick=this.increaseLikes.bind(this)
                    onClick=() => this.increaseLikes() //箭头函数确定this指向,第二种方式
                    //原生的事件绑定是onclike、onchange,在react中要使用驼峰形式onChange、onClick
                    //react中事件的处理
                    //1.通过onXxx属性指定事件处理函数(驼峰)。React使用的是自定义(合成)事件,而不是使用的原生DOM事件(为了更好的兼容性);React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)(为了高效)。
                    //2.通过event.target得到发生事件(例如点击、失去焦点等)的DOM元素对象(避免过度使用ref)
                >
                </button>
                <ul>
                	
                        this.state.list.map((item, index) =>  //数组的map方法
                            return <li key=index
                                       onClick=this.handleItemDelete.bind(this, index)
                                       //给方法传递了参数
                                   >
                                	   item
                            	   </li> //必须加key值
                        )
                    
                </ul>
            </Fragment>
        )
    


export default LikesButton

附:

<script>
	function demo() 
        console.log(this)
    
    demo() //Window斜体的Window,是个Window实例对象
    
    const x = demo.bind(a: 1, b: 2) //bind会返回一个新函数;改了函数里的this,改成什么看传了什么,这里传了的是a: 1, b: 2,所以this指的就是a: 1, b: 2。返回的新函数需要调用才能执行
    x() //a: 1, b: 2
</script>

一般会把一部分拆分为函数的形式,例如

<ul>
     this.getTodoItem()  //这里直接执行这个函数就行了,注意这里不用this指向的设置
</ul>

getTodoItem() 
    return this.state.list.map((item, index) => 
        return (
        	<li key=index //key应该放在循环的最外层的元素上,假如外层有个div标签,要将key加在div上
                onClick=this.handleItemDelete.bind(this, index)
            >
                item
        	</li>
        )
    )

setState更新状态的两种写法:

1.setState(stateChange, [callback])------对象式的setState

setChange为状态改变对象(该对象可以体现出状态的改变);callback是可选的回调函数,它在状态更新完毕、界面也更新后(render调用后)才被调用。

2.setState(updater, [callback])------函数式的setState

updater为返回stateChange对象的函数;updater可以接收到state和props;callback是可选的回调函数,它在状态更新完毕、界面也更新后(render调用后)才被调用。

对象式的setState是函数式的setState的简写方式(语法糖),他俩的使用原则(不绝对,以实际情况而定就行)是:

如果新状态不依赖原状态,使用对象方式;
如果新状态依赖于原状态,使用函数方式;

注意:如果需要在setState()执行后获取最新的状态数据,要在第二个callback函数中读取。

附:

<script>
	class Person 
        constructor(name, age) 
            this.name = name,
            this.age = age
        
        study() 
            console.log(this)
        
    
    
    const p1 = new Person('tom', 18)
    p1.study() //Person斜体的Person,实例调用类中方法,方法中的this就是Person实例
    
    const x = p1.study //赋值语句
    x() //undefined,直接调用,为什么不是window,因为类中所有定义的方法,在局部都开启了严格模式,类似于下面例子
    
    //function study() 
    //    'use strict'
    //    console.log(this) //严格模式的this禁止指向window,只能是undefined
    //
</script>
<script>
    class Car 
        constructor(name, price) 
            this.name = name //外部传过来的属性值必须写在构造器中
            this.price = price
            //this.wheel = 4
        
        wheel = 4 //往实例对象上增加一个属性wheel,值为4。不必写在constructor中
    
    	static demo = 100 //给类添加一个属性,第二种方式如下Car.demo = 10
    
    
    const c1 = new Car('奔驰', 800)
    console.log(c1)
    
    //精简形式
    //所以写在constructor中的this.state = ...可以写在constructor外面,直接写state = ...
    //初始化状态
    state = 
        ...
    
        
    //constructor中也可以不写this.increaseLikes = this.increaseLikes.bind(this)改变this指向这句代码了,使用下面的赋值写法,和上面wheel一样,相当于在实例对象上增加一个属性,而之前的写法是在类的原型对象上增加increaseLikes方法
    //自定义方法--要用赋值语句的形式+箭头函数
    increaseLikes = () =>  //这里必须用箭头函数,因为箭头函数没有自己的this,但是如果在箭头函数中使用this这个关键字,找其外层函数的this作为箭头函数的this来使用,this就是外层类的实例对象
        ...
    
    //上面写法使得constructor内部是空了,可以去掉了
    //开发中基本不写构造器
        
    //如果给类自身加一个属性可以这样
    //Car.demo = 10 //给类Car添加属性demo,不是给实例添加
    //可以不写在外面,写在Class里面,如上static
</script>

附:展开运算符

//1展开数组
let arr1 = [1, 2]
let arr2 = [9, 4]
console.log(...arr1) //1, 2  展开一个数组
let arr3 = [...arr1, ...arr2] //用在等号右边

//2用在形参中
function sum(...nums)  //求和,nums是数组[1, 3, 5, 6]
    return nums.reduce(preValue, currentValue) => 
        return preValue + currentValue
    

console.log(sum(1, 3, 5, 6))

//展开运算符不能用于对象
let person = name: 'tom', age: 23
console.log(...person) //报错,因为展开运算符不能用于对象,...person不行,不能像数组一样这样使用
//但是
//...person外面加一层得到...person就可以使用,这是对象的特殊语法
let person2 = ...person //name: 'tom', age: 23,深拷贝
//而且还可以合并
let person3 = name: 'li', grade: 19
let mergePerson = ...person, ...person3 //name: 'li', age: 23, grade: 19  一样的属性后者会覆盖前者
let person4 = ...person, age: 9 //name: 'tom', age: 9


//注意
const p = name: 'tom', age: 16
//展开运算符应用在React组件上为组件添加属性时,这里的...p的不是和上面讲解的对象的自身花括号,而是JSX语法的花括号,其内部必须是js表达式,而...p可以这样应用对象的展开运算符,是因为引入babel和react后就可以这样使用对象的展开运算符,React中对象的特殊语法
//但是这种语法不能随意的使用,比如下面的句子就会不起作用,这种使用方式仅仅适用于标签属性的传递,别的地方都不行
//console.log('@', ...p) //什么也不展示,因为这种语法不能随意的使用,仅仅适用于标签属性的传递,别的地方都不行
ReactDOM.render(<Person ...p/>, document.getElementById('test'))
//其实标签中...p这些就是props,所以传递标签属性也叫传递props,props就是组件的实例对象的所有属性,所以组件取属性值的时候用this.props.name,this是组件的实例对象

附:JS中reduce()用法

数组的reduce函数,用于数组的条件统计、条件求和、筛选最值

//语法
arr.reduce(function(prev,cur,index,arr)
...
, init);
//prev 表示上一次调用回调时的返回值,或者初始值 init;
//cur 表示当前正在处理的数组元素;
//index 表示当前正在处理的数组元素的索引,若提供 init 值,则索引为0,否则索引为1;
//arr 表示原数组;
//init 表示初始值。

    
//实例
var arr = [3,9,4,3,6,0,9];
    
 //1. 求数组项之和
var sum = arr.reduce(function (prev, cur) 
    return prev + cur;
,0);
//传入了初始值0,所以开始时prev的值为0,cur的值为数组第一项3,相加之后返回值为3作为下一轮回调的prev值,然后再继续与下一个数组项相加,以此类推,直至完成所有数组项的和并返回。

//2. 求数组项最大值
var max = arr.reduce(function (prev, cur) 
    return Math.max(prev,cur);
);
//由于未传入初始值,所以开始时prev的值为数组第一项3,cur的值为数组第二项9,取两值最大值后继续进入下一轮回调。

//3. 数组去重
var newArr = arr.reduce(function (prev, cur) 
    prev.indexOf(cur) === -1 && prev.push(cur); //相当于if的写法
    return prev;
,[]);

组件间传值

TodoList.js父组件

父组件通过属性的方式向子组件传值

this.state.list.map((item, index) => 
      return (
        <TodoItem
          key=item //这是循环需要的key
          content=item //父组件通过属性的方式向子组件传值,传过去的值叫content
          index=index //这是父组件向子组件传值
          deleteItem = this.handleItemDelete.bind(this) //父组件向子组件传递方法时必须在有bind指向,因为这样在子组件内部使用deleteItem方法时,this才会指向父组件
        />
)

当组件的 state或者props发生改变的时候,自己的render函数就会重新执行;
当父组件的render函数被运行时,它的子组件的render函数都将重新被允许一次

TodoItem.js子组件

子组件通过 this.props.属性名 的方式来接受值

props是只读的,不能修改

render() 
    return <div onClick=this.handleClick>this.props.content</div>
    //可以把this.props的值用ES6语法解构
    //const  content  = this.props;
    //return <div onClick=this.handleClick>content</div> //这里直接用content


handleClick() 
    this.props.deleteItem(this.props.index) //子组件调用父组件传入的方法,并使用父组件传入的参数
    //实际用下面这种方式
    //const  deleteItem, index  = this.props;
    //deleteItem(index)
    //解构赋值 在各个部位分别做解构赋值,不要全部写在一处

不允许子组件直接修改父组件的内容,但子组件可以调用父组件的方法,通过父组件的方法进而改变父组件里的数据

附:函数式组件因为内部this为undefined,所以不能使用state,refs,但是可以使用propsÿ

以上是关于React的主要内容,如果未能解决你的问题,请参考以下文章

前端学习(3121):react-hello-react的state的简写方式

前端学习(3121):react-hello-react的state的简写方式

react学习---props的简写形式

23.react-router 路由

[react] 为什么建议Fragment包裹元素?它的简写是什么?

React之组件实例的三大属性之props