Easy的React学习笔记(一.基础)

目录

1_开发环境搭建

2_用命令create-react-app (脚手架)创建项目目录demo1

3_通过组件化开发输出helloworld

4_JSX语法

5_小姐姐服务页面(Xiaojiejie.js)编写

6_React响应式设计原理与数据的绑定方法、增加服务项

需求Z:

this.state:react中用来控制组件内部状态的数据

onchange 事件:

7_增加服务项

①让列表数据化

②增加服务项

8_删除服务项

9_jsx中几个要注意的地方:代码注释、css的class陷阱、html解析问题dangerouslySetInnerHTML属性、label标签的for

1.代码注释:

2.jsx中css的class陷阱,应为className

3.html解析问题的:dangerouslySetInnerHTML属性

4.label标签的htmlFor

10_react快速生成代码插件:Simple React Snippets

11_组件拆分

12_父子组件相互传值

1.父组件向子组件传值:在子组件加上属性的形式。

2.子组件操作父组件数据(实际上是父组件向子组件传方法):在子组件加上属性的形式。

父组件向子组件传方法:

①父组件向子组件传值 、子组件向父组件传值(父组件向子组件传方法)都是在子组件加上属性的形式实现的,然后通过this.props.属性名的方式访问。

this.props:

React中,使用外部(被传入传入的)数据(通过 this.props 访问),被传入的数据可在组件中通过 this.props 在 render() 访问。

注释:(props:属性properties)

this.state:

组件还可以维护其内部的有状态的数据(通过 this.state 访问)。

注释:(state :状态;有状态的数据)

②调用方法时要绑定this指向,可以直接在调用处bind绑定,但是更好的是在组件构造函数内绑定,这样有助于性能优化。

13_React的单项数据流、函数式编程

1.单项数据流

2.函数式编程

14_react developer tools的安装与使用

1.安装详见我另一篇博客:

2.react developer tools插件的三种状态

3.使用

由于本学习笔记过长,图片较多,编辑器打字时有些卡顿,所以之后内容写在另一篇博客:


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代码如下(用ReactDOMrender渲染语法把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指向*/} 

参考资料:

MDN,函数的 this 关键字

MDN,bind() 方法

②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来测试程序了,在工作中前端的调试都是在这里进行的。

 

由于本学习笔记过长,图片较多,编辑器打字时有些卡顿,所以之后内容写在另一篇博客:

Easy's React学习笔记(二.进阶)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章