目录
2_用命令create-react-app (脚手架)创建项目目录demo1
this.state:react中用来控制组件内部状态的数据
9_jsx中几个要注意的地方:代码注释、css的class陷阱、html解析问题dangerouslySetInnerHTML属性、label标签的for
3.html解析问题的:dangerouslySetInnerHTML属性
10_react快速生成代码插件:Simple React Snippets
2.子组件操作父组件数据(实际上是父组件向子组件传方法):在子组件加上属性的形式。
①父组件向子组件传值 、子组件向父组件传值(父组件向子组件传方法)都是在子组件加上属性的形式实现的,然后通过this.props.属性名的方式访问。
React中,使用外部(被传入传入的)数据(通过 this.props 访问),被传入的数据可在组件中通过 this.props 在 render() 访问。
组件还可以维护其内部的有状态的数据(通过 this.state 访问)。
②调用方法时要绑定this指向,可以直接在调用处bind绑定,但是更好的是在组件构造函数内绑定,这样有助于性能优化。
14_react developer tools的安装与使用
2.react developer tools插件的三种状态
由于本学习笔记过长,图片较多,编辑器打字时有些卡顿,所以之后内容写在另一篇博客:
1_开发环境搭建
①前往http://nodejs.cn/,找到自己电脑适合的版本下载并安装node.js。
②win键+r(运行),cmd打开命令行。
输入相关命令检查node.js是否安装成功,显示nodejs与npm对应的版本即为安装成功。
安装node主要为了使用npm包管理工具。
③安装官方脚手架工具create-react-app。
若无法安装create-react-app(安装失败),
可尝试更换网络环境后重新安装,若还是失败,排除网络因素后,则以管理员身份运行cmd(命令提示符)后成功安装create-react-app
看到安装了91个包就表示安装成功。
2_用命令create-react-app (脚手架)创建项目目录demo1
创建失败:
解决方法:
同样以管理员身份运行cmd(命令提示符),重新输入命令create-react-app 创建项目demo1
成功用create-react-app创建demo1,可以看到demo1里面有许多脚手架生成的文件。
根据命令提示运行我的第一个react文件
3_通过组件化开发输出helloworld
把src目录里面的文件全部删掉,然后新建index.js(入口文件)与App.js(方法组件,React核心之一就是组件化开发)
入口文件index.js代码如下(用ReactDOM的render渲染语法把APP模块渲染到public\index.html中的root上):
import React from 'react' //要用react所以引入react
import ReactDOM from 'react-dom' //要操作dom所以引入react-dom
import App from './App' //组件化开发,有了App组件后就可以通过reactdom的render方法在public\index.html的rootID上渲染出来
ReactDOM.render(<App />,document.getElementById('root')) //用react语法把App组件render到index.html中id为root的标签上
注意此时index.js中的三句导入都是导入默认组件,React、ReactDOM、App这三个组件名都是自己取得,取ABC也可以,但是为了做到望文生义,以固定写法比较好。其中React、ReactDOM是导入默认组件,App是导入的自定义组件(必须以大写字母开头)。
组件App.js代码如下(React中自定义组件名必须以大写字母开头,例如本例的App组件必须大写字母开头,不然会报错):
import React,{Component} from "react"
// import React from 'react'
// Const Component = React.Component即import {Component} from 'react'
// 这里是es6解构赋值的写法,即Component等于React.Component
class App extends Component{
render(){
return (
<div>
Hello,EasyLee
<ul className='my_list'>
<li>{true ? 'i love coding' : 'i love eating' }</li>
<li>i love react</li>
</ul>
{/* 上述jsx代码相当于如下js代码:
var child1 = React.createElement('li',null,'i love coding');
var chidl2 = React.createElement('li',null,'i love react');
var chidl3 = React.createElement('ul',{className='my_list'},child1,child2); */}
</div>
)
}
}
export default App;//在index.js文件中要引用App组件所以最后要用export default暴露(输出)App组件。
在index.js文件中要引用App组件所以最后要用export default暴露(输出)App组件。
疑问:可以把import React,{Component} from 'react'中的Component改成a吗?
答:不能,Component是react的成员组件,react官方自己定义好的,所以不能锁边取一个名字。
public目录下模板文件index.html代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>EasyLee WEB</title>
</head>
<body>
<noscript>需要开启JavaScript.</noscript>
<div id="root"></div>
</body>
</html>
有几点(①②③④)要注意:
①入口文件index.js与组件App.js导入的头部文件不同,:
入口文件index.js头部通常由三部分组成:导入react默认组件、导入ReactDOM、导入自定义组件。
②入口文件index.js与组件App.js导入的底部代码不同:
入口文件index.js下方把自定义组件(以App为例)通过ReactDOM.render渲染到index.html文件上。
自定义组件App.js底部要通过export default 语句导出。
index.js(入口文件):
import React from "react"//①要用react所以引入react
import ReactDOM from "react-dom"//②要操作dom所以引入react-dom
import App from "./App"//③导入自定义组件
ReactDOM.render(<App />,document.getElementById('root'))
App.js(自定义组件以App.js为例,底部要通过export default 语句导出):
import React,{Component} from "react"
// import React from 'react'
// Const Component = React.Component即import {Component} from 'react'
// 这里是es6解构赋值的写法,即Component等于React.Component
export default App;//底部导出自定义组件
③自定义组件名首字母必须大写,如App。
④牢记自定义组件的语法结构,以App.js组件为例:
import React,{Component} from "react"
class App extends Component{
render(){
return (
<div>
</div>
)
}
}
export default App;//底部导出自定义组件
4_JSX语法
JSX就是Javascript和XML结合的一种格式。React发明了JSX,可以方便的利用HTML语法来创建虚拟DOM,当遇到<
,JSX就当作HTML解析,遇到{
就当JavaScript解析.
import React,{Component} from 'react'
// import React from 'react'
// Const Component = React.Component即import {Component} from 'react'
// 这里是es6解构赋值的写法,即Component等于React.Component
class App extends Component{
render(){
return (
<div>
{/* Hello,EasyLee! */}
<ul className='my_list'>
<li>{true ? 'i love coding' : 'i love eating' }</li>
<li>i love react</li>
</ul>
</div>
)
// 上述jsx代码相当于如下js代码:
// var child1 = React.createElement('li',null,'i love coding');
// var chidl2 = React.createElement('li',null,'i love react');
// var chidl3 = React.createElement('ul',{className='my_list'},child1,child2);
}
}
export default App;
其中 createElement(a, b, c)方法用法如下:
①参数 a:表示元素的类型,比如:h1, div 等。
②参数 b:表示该元素上的属性,使用 JavaScript 对象方式表示。
③参数 c:表示该元素内部的内容(子元素),可以是文字,可以继续嵌套另外一个 React.createElement(a, b, c)
。
这种方法其实在实际 React 开发中几乎不会使用,因为可以直接用JSX语法 。
5_小姐姐服务页面(Xiaojiejie.js)编写
疑问:为何每个自定义组件都要引入React?
答:因为webpack在解析jsx的时候会把下面代码的Component解析成React.Component这个方法,假如他发现没有引入React,就会报错。
Xiaojiejie.js代码如下:
import React,{Component} from 'react'
class Xiaojiejie extends Component{
render(){
return (
<div>
<input /><button>增加服务</button>
<ul>
<li>服务1</li>
<li>服务2</li>
</ul>
</div>
)
}
}
export default Xiaojiejie;
在index.js中加入如下代码:
ReactDOM.render(<Xiaojiejie />,document.getElementById('xiaojiejie'))
在index.html中加入如下代码:
<div id="xiaojiejie"></div>
加入Xiaojiejie.js后运行效果如图所示:
问:由于React要求必须在一个组件的最外层进行包裹,如下图的光标处的div,不加就会报错,但是在Flex布局中,return内最外层包裹div会影响布局,Flex布局中最外层不能有包裹,这时候怎么办呢?
答:Flex布局中<div>由<Fragment>替代,不过记得要在头部多引入react的成员组件Fragment(注意成员组件Fragment的首字母是大写的,引入和使用Fragment标签时要注意):
import React,{Component,Fragment } from 'react'
class Xiaojiejie extends Component{
render(){
return(
<Fragment> {/*注意Fragment标签首字母大写*/}
<div>
<input />
<button>增加服务</button>
</div>
<ul>
<li>服务1</li>
<li>服务2</li>
</ul>
</Fragment>
)
}
}
6_React响应式设计原理与数据的绑定方法、增加服务项
React如此受欢迎的原因(React响应式设计原理):
React不建议直接操作dom,而是让你通过数据的改变从而自动帮你完成界面的改变,所以程序员无需关心dom操作只关心数据操作即可,大大加快了开发速度。
需求Z:
点击增加服务按钮就能在下方列表自动增加小姐姐的服务项(为了实现需求Z,需求Z又拆分为步骤ABCD,在小节6_中还没完全实现需求Z,待小节7_中完善)。
实现Z:
{步骤A(分为①②):在Xiaojiejie组件中添加构造函数,然后在构造函数constructor内定义数据。
①构造函数constructor(一般是固定写法必须背下来, =:等标点符号要注意别写错):
constructor(props){
super(props)//调用父类方法(固定写法)
this.state={
inputValue : '233',//input里的值,注意符号是:不是=,因为inputValue、list都是this.state的属性,属性间用逗号隔开
list:[]//服务列表
}
}
this.state:react中用来控制组件内部状态的数据
②数据绑定(React中的数据绑定采用字面量{ }的形式,上文提到jsx就是遇到{ }当js代码解析、遇到<>当html解析,{ }其实就是在jsx中使用js代码时做的声明):
<input value={this.state.inputValue} />
需求A结束}
由于我们在①中强行将inputValue 的数据绑定为233是写死的,所以在文本框中输入任何东西都是没反应的,如图所示:
而且打开控制台发现报错,而且键盘输入控制台都没反应,因为已经报错了:
控制台提示:错误可设置onChange属性,这个错误可以设置onChange值使控制台恢复正常。
{需求B:改正控制台的报错
需求B的实现:为input标签设置onChange事件,绑定响应事件inputChange解决控制台报错(点击查看onChange事件用法)
<input value={this.state.inputValue} onChange={this.inputChange} /> {/* input的onChange属性:状态值变化 */}
为input标签嘉善onChange事件后,控制台就不报错了。
onchange 事件:
①定义和用法:onchange 事件会在域的内容改变时发生
②语法:οnchange="SomeJavaScriptCode"
需求B结束}
{需求C:键盘输入能改变inputValue的值(改变文本框中的数据),而不是写死的233占据文本框的位置
需求C的实现(分为步骤C1.C2):
步骤C1:首先让键盘输入能输出到控制台中,
在render函数下方,加入响应事件inputChange(),并把onChange属性绑定新写的响应事件inputChange(),让控制台能输出数据
inputChange(e){ //绑定响应事件改变文本框inputValue的值
console.log(e);
}
此时在文本框中输入时控制台就有反应了,在控制台中会输出很多内容了,这些内容就是e,如下面两张图所示:
此时响应事件能用了,那如何获取从键盘输入的值?
{步骤C2:通过e.target.value获取从键盘输入的值(注意e.target.value就是inputValue的值,即e.target.value等于写死的233+键盘键入值)
inputChange(e){ //绑定响应事件改变文本框inputValue的值
console.log(e.target.value);//15_通过e.target.value打印文本框内的值
}
此时控制台打印出文本框的值,e.target.value就是233加上键入的值(如图所示):
此时想当然的会认为只要将e.target.value的值赋给this.state.inputValue就完事了,就能去掉写死的数据233了,但是这样写是错误的,控制台报错:
inputChange(e){ //绑定响应事件改变文本框inputValue的值
console.log(e.target.value);//15_通过e.target.value打印文本框内的值
this.state.inputValue=e.target.value;//错误写法:错误①this指向错误②没用this.setState方法
}
this.state.inputValue=e.target.value这种写法犯了两个错误(①②):
①this指向不正确,所以用bind重新绑定指向(ES5语法):
<input value={this.state.inputValue} onChange={this.inputChange.bind(this)} /> {/* input的onChange属性:状态值变化;bind重新绑定this指向*/}
参考资料:
②React中,改变组件内部的状态数据的值不能想当然的 this.state.inputValue=e.target.value(错误写法)这样写,需要用到this.setState()方法:
inputChange(e){ //绑定响应事件改变文本框inputValue的值
// console.log(e);
// console.log(e.target.value);//15_通过e.target.value打印文本框内的值
// this.state.inputValue=e.target.value;//错误写法:错误①this指向错误②没用this.setState方法
this.setState({ //React中改变组件内部的状态数据的值要用this.setState方法,注意setState的的大小写
inputValue:e.target.value
})
}
此时在原本固定的inputValue为233的文本框中输入可改变文字了,如图所示:
步骤C2结束}
步骤C结束}
7_增加服务项
{需求D:完善构造函数,让render中写死的列表数据化、增加服务项。
需求D的实现(①②):
①让列表数据化
先在构造函数内为list数组增加两个数组元素,代码如下:
constructor(props){
super(props)//调用父类方法(固定写法)
this.state = {
inputValue:'233',//input里的值,注意符号是:不是=,因为inputValue、list都是this.state的属性,属性间用逗号隔开
list:['服务1','服务2'] //服务列表,记得加单引号,不然报错了
}
}
构造函数中有了数据之后,用JavaScript Array map() 方法和箭头函数循环输出列表数据,代码如下:
render(){
return(
<Fragment> {/*注意Fragment标签首字母大写*/}
<div>
<input value={this.state.inputValue} onChange={this.inputChange.bind(this)} /> {/* input的onChange属性:状态值变化;bind重新绑定this指向*/}
<button>增加服务</button>
</div>
<ul>
{/* <li>服务1</li>
<li>服务2</li> */}
{/* 技术胖写法 */}
{
this.state.list.map((item,index)=>{
return <li>{item}</li>
})
}
{/* {
this.state.list.map(
(item)=>{return <li key={item}>{item}</li>}
)
} */}
</ul>
</Fragment>
)
}
箭头函数中有两个参数:
一.item是循环的每一个子项
二.index是索引
服务列表动态循环输出了,实现了列表数据化,但是发现控制台报错(一会解决):
报错意思是list要有一个key值,暂用index+item来实现:
<ul>
{/* 原本写死的列表数 */}
{/* <li>服务1</li>
<li>服务2</li> */}
{/* Easy写法 */}
{/* {
this.state.list.map(
(item)=>{return <li key={item}>{item}</li>}
)
} */}
{/* 技术胖写法 */}
{
this.state.list.map((item,index)=>{
return <li key={item+index}>{item}</li>
})
}
</ul>
报错解决。
点击:查看JavaScript Array map方法(es6)、箭头函数用法。
一个好的经验法则是:在 map()
方法中的元素需要设置 key 属性(https://zh-hans.reactjs.org/docs/lists-and-keys.html#gatsby-focus-wrapper)
疑问:为什么map方法内第一个参数就是当前元素的值,第二个参数就是索引呢?
答:详见Array.map方法的用法
②增加服务项
在增加按钮上先绑定一个新写的addList方法,
<button onClick={this.addList.bind(this)}>增加服务</button>
在之前的inputChange方法后新写addList方法:
addList(){ //为按钮绑定增加服务项事件
this.setState({
list:[...this.state.list,this.state.inputValue]
})
}
...
这个是ES6的新语法,名为扩展运算符。作用把list数组进行了分解,形成了新的数组,然后再进行组合。这种写法更简单和直观,所以推荐这种写法。
点击添加服务按钮可以实现添加文本框中的数据到服务列表了。
理论上每次增加服务项之后,文本框应该清空,只需要在list下面加上如下代码即可:
inputValue:''
addList方法最终代码:
addList(){ //为按钮绑定增加服务项事件
this.setState({
list:[...this.state.list,this.state.inputValue], //...这个是ES6的新语法,名为扩展运算符。作用把list数组进行了分解,形成了新的数组,然后再进行组合。这种写法更简单和直观,所以推荐这种写法。
inputValue:''
})
}
需求D结束}
至此小节6_中的需求Z通过ABCD成功实现。
8_删除服务项
需求:鼠标点击服务项,自动删除。
实现:
要删除一个东西,就先获取数组下标下标。然后为列表添加点击事件,并为点击事件绑定删除方法deleteItem。
为点击事件绑定删除方法deleteItem有两种方法:普通方法、箭头函数方法,区别就在于是否需要绑定this,如下代码所示:
//return加了括号,下面的jsx就可以换行写,看的更清楚
<ul>
{/* 原本写死的列表数 */}
{/* <li>服务1</li>
<li>服务2</li> */}
{/* Easy写法 */}
{/* {
this.state.list.map(
(item)=>{return <li key={item}>{item}</li>}
)
} */}
{/* 技术胖写法 */}
{
this.state.list.map((item,index)=>{
return( //return加了括号,下面的jsx就可以换行写,看的更清楚
//普通写法
<li key={item+index} onClick={this.deleteItem.bind(this,index)}>
{item}
</li>
// 箭头函数写法不用绑定this,因为箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this。
// <li key={item+index} onClick={()=>this.deleteItem(index)}>
// {item}
// </li>
)
})
}
</ul>
deleteItem(index){ //删除列表服务事件
// console.log(index)//测试点击列表项能否取得相应下标
// this.state.list.splice(index,1)错误写法
// this.setState({
// list:this.state.list
// }) 错误写法,即便也能和下面代码实现看似一样的效果,但是react中禁止直接修改state的值,后期优化会有性能问题
let list = this.state.list
list.splice(index,1)
this.setState({
list:list
})
}
9_jsx中几个要注意的地方:代码注释、css的class陷阱、html解析问题dangerouslySetInnerHTML
属性、label标签的for
1.代码注释:
vscode中代码注释快捷键:ctrl+/
详见本人的另一篇博客:
jsx代码注释格式规范最全总结
————————————————
版权声明:本文为CSDN博主「Easy_Lee_willpower」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_37279880/article/details/105885633
2.jsx中css的class陷阱,应为className
在src文件中新建style.css文件,:
style.css中的代码:
.input {
border:3px solid #00FF00;
}
在Xiaojiejie.js头部导入该文件:
import './style.css'
在input标签上加上class属性(这种写法是错误的):
<input class='input' value={this.state.inputValue} onChange={this.inputChange.bind(this)} /> {/* input的onChange属性:状态值变化;bind重新绑定this指向*/}
warning警告,为了防止css的class与js中的class冲突,所以要把input中的class属性改成className:
input标签修改后:
<input className='input' value={this.state.inputValue} onChange={this.inputChange.bind(this)} /> {/* input的onChange属性:状态值变化;bind重新绑定this指向*/}
3.html解析问题的:dangerouslySetInnerHTML
属性
想在文本框输入<h2>标签后,在按下增加服务后能直接渲染出来,而不是如下图效果:
通过dangerouslySetInnerHTML 属性实现:
在li中加入dangerouslySetInnerHTML 属性,代码如下:
<li
key={item+index}
nClick={this.deleteItem.bind(this,index)}
dangerouslySetInnerHTML={{__html:item}}
>
</li>
4.label标签的htmlFor
需求:点击增加服务就能激活文本框
实现代码如下:
<label for='addList'>增加服务</label>
<input id='addList' className='input' value={this.state.inputValue} onChange={this.inputChange.bind(this)} />
控制台警告:
此时把label的for改成htmlFor即可,代码如下:
<label htmlFor='addList'>增加服务</label>
<input id='addList' className='input' value={this.state.inputValue} onChange={this.inputChange.bind(this)} /> {/* input的onChange属性:状态值变化;bind重新绑定this指向*/}
原因:怕DOM操作的htmlFor与js的for循环混淆。
10_react快速生成代码插件:Simple React Snippets
安装步骤:
1.点击扩展插件
2.搜索Simple React Snippets
3.安装install
安装后就可通过对应的Snippets(片段)快速生成(渲染render)大段代码:
11_组件拆分
需求:实际开发中,会把不同功能的组件拆分开写,本项目把服务列表组件拆分。
实现:
在src目录下新建Xiaojiejie组件的子组件XiaojiejieItem.js代码如下:
import React, { Component } from 'react';
class XiaojiejieItem extends Component {
render() {
return (
<li>子组件</li>
// <li>{this.props.content}</li>
);
}
}
export default XiaojiejieItem;
在Xiaojiejie.js头部引入子XiaojiejieItem组件:
import XiaojiejieItem from './XiaojiejieItem'
并修改ul内代码,Xiaojiejie.js代码如下:
import React,{Component,Fragment} from "react"
import './style.css'
import XiaojiejieItem from './XiaojiejieItem'
class Xiaojiejie extends Component{
constructor(props){
super(props)//调用父类方法(固定写法)
this.state = {
inputValue:'',//input里的值,注意符号是:不是=,因为inputValue、list都是this.state的属性,属性间用逗号隔开
list:['服务1','服务2'] //服务列表,记得加单引号,不然报错了
}
}
render(){
return(
<Fragment> {/*注意Fragment标签首字母大写*/}
<div>
<label htmlFor='addList'>增加服务</label>
<input id='addList' className='input' value={this.state.inputValue} onChange={this.inputChange.bind(this)} /> {/* input的onChange属性:状态值变化;bind重新绑定this指向Xiaojiejie*/}
<button onClick={this.addList.bind(this)}>增加服务</button>
</div>
<ul>
{/* 原本写死的列表数 */}
{/* <li>服务1</li>
<li>服务2</li> */}
{/* Easy写法 */}
{/* {
this.state.list.map(
(item)=>{return <li key={item}>{item}</li>}
)
}
*/}
{/* 技术胖写法 */}
{
this.state.list.map((item,index)=>{
return(
// return加了括号,下面的jsx就可以换行写,看的更清楚
// 普通写法
// <li
// key={item+index}
// onClick={this.deleteItem.bind(this,index)}
// dangerouslySetInnerHTML={{__html:item}}
// >
// </li>
// 箭头函数写法:不用绑定this,因为箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this。
// <li key={item+index} onClick={()=>this.deleteItem(index)}>
// {item}
// </li>
// 拆分成子组件组件写法
<XiaojiejieItem />
)
})
}
</ul>
</Fragment>
)
}
inputChange(e){ //绑定响应事件改变文本框inputValue的值
// console.log(this);
// console.log(e);//e是一堆内容
// console.log(e.target.value);//15_通过e.target.value打印文本框内的值
// this.state.inputValue=e.target.value;//错误写法:因为没用没用this.setState方法
// console.log(this);//输出undefined,所以要bind方法绑定this
// console.log(this === window);//false
this.setState({ //React中改变组件内部的状态数据的值要用this.setState方法,注意setState的的大小写
inputValue:e.target.value
})
}
addList(){ //为按钮绑定增加服务项事件
this.setState({
list:[...this.state.list,this.state.inputValue], //...这个是ES6的新语法的扩展运算符,名为扩展运算符。作用把list数组进行了分解,形成了新的数组,然后再进行组合。这种写法更简单和直观,所以推荐这种写法。
inputValue:''
})
}
deleteItem(index){ //删除列表服务事件
// console.log(index)//测试点击列表项能否取得相应下标
// this.state.list.splice(index,1)错误写法
// this.setState({
// list:this.state.list
// }) 错误写法,即便也能和下面代码实现看似一样的效果,但是react中禁止直接修改state的值,后期优化会有性能问题
let list = this.state.list
list.splice(index,1)
this.setState({
list:list
})
}
}
export default Xiaojiejie;
此时由于Array.map循环内是子组件XiaojiejieItem,而XiaojiejieItem中的li里面的代码又是写死的,所以页面如下:
在12_父子组件相互传值的父组件向子组件传值中,解决li数据写死问题。
12_父子组件相互传值
1.父组件向子组件传值:在子组件加上属性的形式。
步骤一:在Xiaojiejie.js的子组件XiaojiejieItem中加入了content={item}属性:
<ul>
{/* 原本写死的列表数 */}
{/* <li>服务1</li>
<li>服务2</li> */}
{/* Easy写法 */}
{/* {
this.state.list.map(
(item)=>{return <li key={item}>{item}</li>}
)
}
*/}
{/* 技术胖写法 */}
{
this.state.list.map((item,index)=>{
return(
// return加了括号,下面的jsx就可以换行写,看的更清楚
// 普通写法
// <li
// key={item+index}
// onClick={this.deleteItem.bind(this,index)}
// dangerouslySetInnerHTML={{__html:item}}
// >
// </li>
// 箭头函数写法:不用绑定this,因为箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this。
// <li key={item+index} onClick={()=>this.deleteItem(index)}>
// {item}
// </li>
// 拆分成子组件组件写法
<XiaojiejieItem content={item} />
)
})
}
</ul>
步骤二:在XiaojiejieItem.js子组件中,通过this.props.属性名的方式接收父组件传递过来的值。
import React, { Component } from 'react';
class XiaojiejieItem extends Component {
render() {
return (
<li>{this.props.content}</li>
);
}
}
export default XiaojiejieItem;
在父组件Xiaojiejie引用子组件XiaojiejieItem时为XiaojiejieItem加入了content={item}属性,又在XiaojiejieItem.js子组件XiaojiejieItem的li中通过this.props.content代码接收,解决了11_组件拆分中数据写死的问题。
运行结果(li中数据不再写死):
2.子组件操作父组件数据(实际上是父组件向子组件传方法):在子组件加上属性的形式。
功能需求:
点击服务项实现删除。该功能原本已经实现了,不过现在组件拆分了,就涉及了子组件向父组件传递数据,实际上还是靠父组件传递方法给子组件,子组件再用父组件的方法来操作父组件的数据。
步骤一:在Xiaojiejie.js的子组件XiaojiejieItem中加入了index、key属性,代码如下:
<ul>
{/* 原本写死的列表数 */}
{/* <li>服务1</li>
<li>服务2</li> */}
{/* Easy写法 */}
{/* {
this.state.list.map(
(item)=>{return <li key={item}>{item}</li>}
)
}
*/}
{/* 技术胖写法 */}
{
this.state.list.map((item,index)=>{
return(
// return加了括号,下面的jsx就可以换行写,看的更清楚
// 普通写法
// <li
// key={item+index}
// onClick={this.deleteItem.bind(this,index)}
// dangerouslySetInnerHTML={{__html:item}}
// >
// </li>
// 箭头函数写法:不用绑定this,因为箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this。
// <li key={item+index} onClick={()=>this.deleteItem(index)}>
// {item}
// </li>
// 拆分成子组件组件写法
<XiaojiejieItem
content={item}
index={index}
key={item+index}
/>
)
})
}
</ul>
步骤二:在XiaojiejieItem.js子组件中为li添加点击事件handleClick,handleClick中测试是否取得数组index代码如下:
import React, { Component } from 'react';
class XiaojiejieItem extends Component {
render() {
return (
<li onClick={this.handleClick}>{this.props.content}</li>
);
}
handleClick(){
console.log(this.props.index);
}
}
export default XiaojiejieItem;
实现效果如下:
点击服务项后报错:
即handleClick方法中的this未重新指向XiaojiejieItem ,所以要用bind重新绑定this指向:
<li onClick={this.handleClick.bind(this)}>{this.props.content}</li>
报错消失,并实现点击子组件服务项取得数组下标。
构造函数里绑定this:
在构造函数里绑定this指向。这样绑定性能会高一些,特别是在高级组件开发中,会有很大的作用。XiaojiejieItem 的构造函数代码如下:
constructor(props){
super(props)
this.handleClick=this.handleClick.bind(this)
}
步骤三:通过操作子组件删除父组件里的数据。
实现:React有明确规定,子组件时不能操作父组件里的数据的,所以子组件需要借用一个父组件的方法来修改父组件的内容。
即子组件XiaojiejieItem借用父组件Xiaojiejie的deleteItem方法,现在要作的就是子组件调用这个方法。
父组件向子组件传方法:
如果子组件要调用父组件方法,其实和传递数据差不多,父组件向子组件传方法,也是在引用子组件时上加属性:
在Xiaojiejie.js的子组件XiaojiejieItem中加入了deleteItem属性:
<XiaojiejieItem
content={item}
index={index}
key={item+index}
deleteItem={this.deleteItem.bind(this)}
/>
记得这里也要进行this
的绑定,如果不绑定子组件是没办法找到这个父组件的方法的。
XiaojiejieItem 的handleClick方法代码如下:
handleClick(){
// console.log(this.props.index)
this.props.deleteItem(this.props.index)
}
实现效果:
到此为止,就算是实现了子组件向父组件传值。
Xiaojiejie.js完整代码如下:
import React,{Component,Fragment} from "react"
import './style.css'
import XiaojiejieItem from './XiaojiejieItem'
class Xiaojiejie extends Component{
constructor(props){
super(props)//调用父类方法(固定写法)
this.state = {
inputValue:'',//input里的值,注意符号是:不是=,因为inputValue、list都是this.state的属性,属性间用逗号隔开
list:['服务1','服务2'] //服务列表,记得加单引号,不然报错了
}
}
render(){
return(
<Fragment> {/*注意Fragment标签首字母大写*/}
<div>
<label htmlFor='addList'>增加服务</label>
<input id='addList' className='input' value={this.state.inputValue} onChange={this.inputChange.bind(this)} /> {/* input的onChange属性:状态值变化;bind重新绑定this指向Xiaojiejie*/}
<button onClick={this.addList.bind(this)}>增加服务</button>
</div>
<ul>
{/* 原本写死的列表数 */}
{/* <li>服务1</li>
<li>服务2</li> */}
{/* Easy写法 */}
{/* {
this.state.list.map(
(item)=>{return <li key={item}>{item}</li>}
)
}
*/}
{/* 技术胖写法 */}
{
this.state.list.map((item,index)=>{
return(
// return加了括号,下面的jsx就可以换行写,看的更清楚
// 普通写法
// <li
// key={item+index}
// onClick={this.deleteItem.bind(this,index)}
// dangerouslySetInnerHTML={{__html:item}}
// >
// </li>
// 箭头函数写法:不用绑定this,因为箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this。
// <li key={item+index} onClick={()=>this.deleteItem(index)}>
// {item}
// </li>
// 拆分成子组件组件写法
<XiaojiejieItem
content={item}
index={index}
key={item+index}
deleteItem={this.deleteItem.bind(this)}
/>
)
})
}
</ul>
</Fragment>
)
}
inputChange(e){ //绑定响应事件改变文本框inputValue的值
// console.log(this);
// console.log(e);//e是一堆内容
// console.log(e.target.value);//15_通过e.target.value打印文本框内的值
// this.state.inputValue=e.target.value;//错误写法:因为没用没用this.setState方法
// console.log(this);//输出undefined,所以要bind方法绑定this
// console.log(this === window);//false
this.setState({ //React中改变组件内部的状态数据的值要用this.setState方法,注意setState的的大小写
inputValue:e.target.value
})
}
addList(){ //为按钮绑定增加服务项事件
this.setState({
list:[...this.state.list,this.state.inputValue], //...这个是ES6的新语法的扩展运算符,名为扩展运算符。作用把list数组进行了分解,形成了新的数组,然后再进行组合。这种写法更简单和直观,所以推荐这种写法。
inputValue:''
})
}
deleteItem(index){ //删除列表服务事件
// console.log(index)//测试点击列表项能否取得相应下标
// this.state.list.splice(index,1)错误写法
// this.setState({
// list:this.state.list
// }) 错误写法,即便也能和下面代码实现看似一样的效果,但是react中禁止直接修改state的值,后期优化会有性能问题
let list = this.state.list
list.splice(index,1)
this.setState({
list:list
})
}
}
export default Xiaojiejie;
XiaojiejieItem.js完整代码如下:
import React, { Component } from 'react';
class XiaojiejieItem extends Component {
constructor(props){
super(props)
this.handleClick=this.handleClick.bind(this)
}
render() {
return (
<li onClick={this.handleClick}>{this.props.content}</li>
)
}
handleClick(){
// console.log(this.props.index)
this.props.deleteItem(this.props.index)
}
}
export default XiaojiejieItem;
总结:
①父组件向子组件传值 、子组件向父组件传值(父组件向子组件传方法)都是在子组件加上属性的形式实现的,然后通过this.props.属性名的方式访问。
this.props:
React中,使用外部(被传入传入的)数据(通过 this.props 访问),被传入的数据可在组件中通过 this.props 在 render() 访问。
注释:(props:属性properties)
this.state:
组件还可以维护其内部的有状态的数据(通过 this.state 访问)。
注释:(state :状态;有状态的数据)
②调用方法时要绑定this指向,可以直接在调用处bind绑定,但是更好的是在组件构造函数内绑定,这样有助于性能优化。
13_React的单项数据流、函数式编程
1.单项数据流
为Xiaojiejie.js的子组件XiaojiejieItem加入list属性:
<XiaojiejieItem
content={item}
index={index}
key={item+index}
deleteItem={this.deleteItem.bind(this)}
// 单向数据流
list={this.state.list}
/>
XiaojiejieItem.js的handleClick方法加入this.props.list=[]:
handleClick(){
// console.log(this.props.index)
this.props.list=[]
this.props.deleteItem(this.props.index)
}
点击列表项后报错:
意思是父组件传递给子组件的值是只读的,不能在子组件中直接修改父组件的值。如果想在子组件修改父组件的值,只能通过12_父子组件相互传值中父组件引用子组件时,给子组件传递父组件的方法(父组件引用子组件时为子组件增加属性),再用父组件的方法才能修改父组件中的值。
2.函数式编程
在面试React时,经常会问道的一个问题是:函数式编程的好处是什么?
①函数式编程让我们的代码更清晰,每个功能都是一个函数。
②函数式编程为我们的代码测试代理了极大的方便,更容易实现前端自动化测试。
React框架也是函数式编程,所以说优势在大型多人开发的项目中会更加明显,让配合和交流都得心应手。
14_react developer tools的安装与使用
1.安装详见我另一篇博客:
React开发环境(Chrome浏览器)安装react developer tools的两个方法,Download the React DevTools for a better experience
https://blog.csdn.net/qq_37279880/article/details/105975542
2.react developer tools插件的三种状态
插件有三种状态
灰色:说明页面不是React写的
黑色:说明页面是React写的,并且处于生成环境当中。
红色:说明页面是用React编写的,并且处于调试环境当中。
3.使用
打开开发者工具,发现Components,这里你可以清晰的看到React的结构,让自己写的代码更加清晰,你还可以看到组件之间的数据传递,再也不用通过console.log
来测试程序了,在工作中前端的调试都是在这里进行的。
由于本学习笔记过长,图片较多,编辑器打字时有些卡顿,所以之后内容写在另一篇博客: