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文件。
里面引入了react、react-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的简写方式