Hello React
- 通过create-react-app创建一个react项目
- 新建的项目结构如下:
- 下面我们删除src下的所有文件,然后创建一个index.js文件(默认必须使用index命名)
- 接下来我们需要在index.js文件内引入react、react-dom这两个模块,然后调用react-dom的render方法创建react组件并渲染到指定的页面dom节点上
/** * 引入react,因为在react项目中需要用到react的一种语法----JSX ---- js+xml * 引入react-dom,该模块可以帮助我们将react组件渲染到页面上 * 通过调用react-dom模块的render方法实现将react组件渲染并构建dom树,然后将该组件插入到指定的元素上 * ReactDom.render() --该方法两个参数: * 第一个参数就是要被插入的内容 * 第二个参数就是选择被插入的元素 */import React from 'react';import ReactDom from 'react-dom'; ReactDom.render( <h1>hello react</h1>, document.getElementById('root') )
JSX语法与组件
- JSX语法实现了将HTML和JavaScript混合,然后通过编译器将HTML转换到JavaScript当中去,最后由浏览器执行。
- 编译过程由Babel的jsx编译器实现
- React使用JSX来代替常规的JavaScript
- JSX是一个看起来像xml的JavaScript语法控制,在React中会被Babel编译为JavaScript
JSX编写格式
- React的JSX是使用大写小写来区分本地组件和html组件的
- 在React的JSX中编写html的时候,对于标签内的for、class、style这三个属性存在区别,需要写为htmlFor、className、style等号后需要使用双花括号表示
import React, { Component } from 'react'
var MyImg = require('./assets/01.jpg')
export default class App2 extends Component {
render() {
return (
<>
<!--注意这里的htmlFor className style={{...}} src={{}} -->
<label htmlFor="ipt">label</label>
<input type="text" id="ipt" className="ipt" style={{background: 'pink', color: 'green'}} />
<hr/>
<img src={MyImg} alt=""/>
</>
)
}
}
JSX原理
原理:对于jsx,在编译过程中会将我们在render方法的第一个参数中编写的HTML的dom块转换为用js的表示,也就是通过调用React.createElement()去实现对dom树的修改。
可以对比一下以下两段代码,能明白了。(需要注意的是,以下这两段代码都是可以正常运行且效果是一样的,只是我们一般直接写html即可)
ReactDom.render( <div id='reactDiv' class='elMM'> hello react </div>, document.getElementById('root') )
ReactDom.render( React.createElement( "div", { id:"reactDiv", class:"MM" }, "hello react" ), document.getElementById('root') )
JSX编译流程
- 使用React构建Dom组件
- Babel对其进行编译,将jsx文件编译为js对象
- 然后运行该js,调用到render的方法去将Dom组件插入到页面中
Class组件
通过在src下创建js文件并在该文件内使用class声明一个组件类(该组件需要基础React.Component),然后通过重写父类的render方法来返回一个组件模板,最后使用export default导出该组件。
然后在需要用到该组件的js文件内导入该文件,并使用render方法将该组件渲染到页面的DOM树上。
- 创建组件
import React from 'react'
export default class App extends React.Component{
render(){
return <div>class Component </div>
}
}
- 引入组件并使用
import React from 'react';
import ReactDom from 'react-dom';
import App from'./Component/app.js'
ReactDom.render(
<App/>,
document.getElementById('root')
)
注意:
- 定义组件的时候,return后面只能有一个根标签。
- 使用组件的时候,首字母必须大写
- 如果定义组件的时候最外层根标签不想使用div,可以使用<></>代替
函数式组件
函数式组件顾名思义就是,通过函数来讲html块返回。
export default function hello(){
return (
<div style={{color:'blue'}}>
函数式组件hello
</div>
)
}
同样的,我们只需要在使用这个组件的js文件内引入该组件使用即可。如下
import React from 'react';
import ReactDom from 'react-dom';
import Hello from './Methor-Component/hello.js'
ReactDom.render(
<div>
<Hello></Hello>
</div>,
document.getElementById('root')
)
组件的样式
行内样式
首先我们需要知道的是,如果在jsx的html模板中使用行内样式的话,赋予style的应该是一个对象形式的模板值。
- 写法一
export default function hello(){ return ( <div style={{color:'blue'}}> 函数式组件hello </div> )}
- 写法二
export default function hello(){ let obj={ color:'blue' } return ( <div style={obj}> 函数式组件hello </div> )}
需要注意的是:如果我们使用到的样式属性是例如background-color这种形式的,我们在jsx中必须使用驼峰命名的方式书写,如下
export default function hello(){ //定义变量 let name='函数式组件hello' let obj={ //注意这里--修改为驼峰命名法的方式 backgroundColor:'blue' } return ( <div style={obj}> name={name} {/*注意这里--修改为驼峰命名法的方式 */} <div style={{backgroundColor:'red'}}></div> </div> ) }
外部样式
为组件编写外部css样式文件之后,我们只需要在对应的组件js文件中import对应的css文件即可
事件绑定
在节点标签内定义函数
例如我们对div块绑定点击事件----onClick={()=>{…}}
在class组件内
如果在指定事件函数的时候打上括号,那么在页面在加载的时候该函数会被自动执行
在函数式组件内
react事件绑定与普通的事件绑定的区别
- react并不会把事件真正绑定到具体的节点上,而是采用事件代理的方法
关于ES6改变this指向
- call
- apply
- bind
call
改变this指向并自动执行对应的函数
var obj1={
name='obj1',getname(){
console.log(this.name)}}var obj2={
name='obj2',getname(){
console.log(this.name)}}
obj1.getname().call(obj2)//控制台输出的是obj2
apply
改变this指向并自动执行对应的函数
var obj1={
name='obj1',getname(){
console.log(this.name)}}var obj2={
name='obj2',getname(){
console.log(this.name)}}
obj1.getname().apply(obj2)//控制台输出的是obj2
bind
改变this指向,当不会自动执行,如果需要自动执行,则需要在后面加一个括号
obj2.getname().bind(obj1)()
Ref的应用
通过ref获取节点元素信息
通过给标签设置ref,然后通过this.refs.username,就可以获取对应节点元素ref对象
- 设置input的ref为inputRef,然后通过调用this.refs.inputRef.value就可以获取到input框的内容了
import React, { Component } from 'react'
export default class REF extends Component {
render() {
return (
<div>
<input ref='inputRef' />
<button onClick={ ()=>{ this.getInputData() } } >获取input</button>
</div>
)
}
getInputData(){
//通过给input设置ref,然后调用refs中对应的ref就可以获取到input框的ref对象
console.log(this.refs.inputRef.value);
}
}
采用react.createRef()创建ref对象
由于在this.ref存在被弃用的问题,所以react为我们通过了createRef方法去创建ref对象,创建之后我们只需要在对应的节点标签中设置ref的时候引用该对应,即可将该ref对象指向该节点,然后我们就可以通过调用该对象去获取对应节点的ref信息
import React, { Component } from 'react'
export default class CreateRef extends Component {
//通过react创建一个ref对象,然后只需要在对应的节点标签设置ref的时候引用这个对象,即可通过该ref对象去获取对应节点的信息
input = React.createRef()
render() {
return (
<div>
{/* 设置input的ref对象为input */}
<input ref={this.input} />
<button onClick={()=>{ this.getInput()}} >获取input</button>
</div>
)
}
getInput(){
//获取input的值
console.log(this.input.current.value);
}
}
组件数据挂载方式
状态(state)
什么是状态state
- 状态state是组件内部一组特有的数据,以一个对象形式存在。
- 状态存放了该组件用于渲染的数据
- 状态还承担着管理该组件被渲染数据的功能
- 可以通过state去把变量绑定在组件内部,也可以通过setstate方法去对状态内的数据变量进行修改从而在组件上渲染响应
定义state
在class组件内直接声明state
import React, { Component } from 'react'
export default class StateTest extends Component {
constructor(){
state:{
name:'stateComName'
}
render() {
return (
<div>
{/* 点击按钮,修改状态state */}
<button onClick={()=>{
this.setState({name:'upData'})
}}>
up
</button>
{/* 渲染state内的name到页面上 */}
{this.state.name}
</div>
)
}
}
通过构造方法声明
import React, { Component } from 'react'
export default class StateTest extends Component {
constructor(){
super()
//创建state
this.state={
name:'stateComName'
}
}
render() {
return (
<div>
{/* 点击按钮,修改状态state */}
<button onClick={()=>{
this.setState({name:'upData'})
}}>
up
</button>
{/* 渲染state内的name到页面上 */}
{this.state.name}
</div>
)
}
}
修改state内的数据变量
通过调用state的setState方法对state内的数据变量修改并通知react重新渲染被修改的数据变量
import React, { Component } from 'react'
export default class StateTest extends Component {
constructor(){
super()
//创建state
this.state={
name:'stateComName'
}
}
render() {
return (
<div>
{/* 点击按钮,修改状态state */}
<button onClick={()=>{
this.setState({name:'upData'})
}}>
up
</button>
{/* 渲染state内的name到页面上 */}
{this.state.name}
</div>
)
}
}
setState是异步的
如果setState的调用处于一个异步逻辑,则会变成同步
如果setState的调用处于一个同步逻辑,则会变成异步
setState的第二个参数–回调函数
当setState中对于state修改操作的异步执行之后,就会调用该第二个参数的回调函数
this.setState({
.....
},()={
console.log('回调函数')
})
函数式组件内的state
//引入userState模块
import React,{ useState } from 'react'
export default function Hooks() {
//创建num的state,nun=0
const [num,setNum] =useState(0)
return (
<div>
{num}
<button onClick={()=>setNum(num+1)}>
click
</button>
</div>
)
}
列表渲染
React中实现列表渲染,与vue不同的是,vue是直接使用v-for的指令去循环遍历一个数组然后动态的在dom上创建节点元素。而React则是通过调用元素JavaScript中Array类型的map方法来实现。
对于Array.map()方法:该方法是通过遍历调用该方法的Array然后,将遍历到的每一项都去执行作为参数传入到该map方法内的函数,最后返回执行结果
也就是说,在React中通过调用Array的map方法去遍历list的每一项元素,然后通过我们的需要将创建html模板的函数作为参数传到map方法中,最后返回对应的html模板,从而实现对列表的循环然后渲染到页面上
import React, { Component } from 'react'
export default class StateTest extends Component {
constructor(){
super()
//创建state
this.state={
list:[1,2,3,4,5]
}
}
render() {
return (
<div>
<ul>
{
this.state.list.map(item => <li>{item}</li>)
}
</ul>
</div>
)
}
}
添加key
为被创建的每个节点添加key
render() {
return (
<div>
<ul>
{
this.state.list.map((item,index) => <li key={index}>{item}</li>)
}
</ul>
</div>
)
}
如果对array.map还是无法理解,可以执行以下的代码
let array=[1,2,3,4,5,6]let mapArray = array.map(item=> item*2)
console.log(mapArray)//[2,4,5,8,10,12]
条件渲染
通过三目运算符去实现
这种方式就类似与vue的v-if指令,通过判断Ture or False来创建渲染节点
export default class IFdemo extends Component {
InputRef=React.createRef()
ModelDemo=`<h1 >条件为true</h1>`
ModelDemox=`<h1>条件渲染为false</h1>`
state={
data:'条件渲染',
TF:true,
}
render() {
return (
<div>
{/*通过判断输入框内数字的大小的条件去将ModelDemo与ModelDemox渲染到页面*/}
<input ref={this.InputRef}></input>
<button onClick={()=>this.check()}>识别</button>
{
this.state.TF?<div dangerouslySetInnerHTML={{__html:this.ModelDemo}}></div>:<div dangerouslySetInnerHTML={{__html:this.ModelDemox}}></div>
}
</div>
)
}
check(){
if(this.InputRef.current.value>10){
this.setState({
TF:true
})
}else{
this.setState({
TF:false
})
}
}
}
通过控制hidden去实现
这种方式就类似于vue的v-show指令,在页面加载的时候就会创建,只是判断ture or false决定是否显示
.ShowClass{display: none;}
import React, { Component } from 'react'
import './Css/IfHidden.css'
export default class IfHidden extends Component {
InputRef=React.createRef()
ModelDemo=`<h1 >条件为true</h1>`
state={
data:'条件渲染',
TF:true,
}
render() {
return (
<div>
<input ref={this.InputRef}></input>
<button onClick={()=>this.check()}>识别</button>
{/*
通过判断TF的值来决定是否将设置有display:none的class选择器设置在该div上
*/}
<div className={this.state.TF?'':'ShowClass'}>
<div dangerouslySetInnerHTML={{
__html:this.ModelDemo
}}></div>
</div>
</div>
)
}
check(){
if(this.InputRef.current.value>10){
this.setState({
TF:true
})
}else{
this.setState({
TF:false
})
}
}
}
dangerouslySetInnerHTML
dangerouslySetInnerHTML作为标签的属性,设置__html属性来将指定的html代码快渲染到到页面中
import React, { Component } from 'react'
export default class dangerouslySetInnerHTML extends Component {
InputRef=React.createRef()
ModelDemo=`<div>
<h1>插入模块</h1>
</div>`
render() {
return (
<div>
<div dangerouslySetInnerHTML={{__html:this.ModelDemo}}></div>
</div>
)
}
}
属性(props)
正常情况下,props是从外部传入到组件内部或者组件内部自己初始化的,但是props不能被组件自己更改,但是可以通过父组件主动重新渲染的方式来传入新的props
如何向子组件传输props
只需要在父组件引用创建子组件的时候,在标签中以属性的方式设置好要传递给该子组件的props即可实现。
- 以下这段代码就是在Father组件内引用了Chile这个组件
- 分别两次在该组件的dom上创建了Chile组件
- 给第一个Chile传递了num=123、name=‘xsdw’ 、idths=‘adda这些个props
- 给第二个Chile传递了xx=33的props
import React, { Component } from 'react' import Chile from '../PropsCom/Chile.js' export default class Father extends Component { render() { return ( <div> <Chile num={123} name={'xsdw'} idths={'adda'} /> <Chile xx={33} /> </div> ) } }
如何在子组件内接收调用父组件传递过来的props
在React的每个组件中都有一个props的对象属性,该属性就是专门用来接收父组件向该组件传递过来的props内容的,从父组件传递过来的所有prop都会放在该props对象内。
所以我们只需要this.props.[objectname]即可调用父组件传递过来的props
import React, { Component } from 'react' export default class Chile extends Component { //将传递过来的props放在state里面 state={ propx:this.props } render() { return ( <div> {/*直接调用props对象来使用*/} {this.props.num} </div> ) } }
子组件中限制传进来的props属性的数据类型
React项目下中提供了一个prop-types模块来支持我们在组件中去限制父组件传过来的props属性的数据类型
- 也就是说相当于通过该模块即可预先给该组件内需要从父组件传递过来的属性进行类型设置。
- 同时,当父组件传递对应属性的值过来的时候会自动检查是否符合预先设置的数据类型,如果不一致则会报错。
实现步骤:
- 先导入import PropTypes from ‘prop-types’ (注意:无需安装,直接引入即可)
- 在子组件中定义静态属性propTypes
//定义静态属性
static propTypes = {
//props属性名:PropTypes.类型
title: PropTypes.类型
}
实际应用
import React, { Component } from 'react'
import PropTypes from 'prop-types'
export default class Sub extends Component {
static propTypes = {
//设置为number类型
num: PropTypes.number
}
render() {
return (
<div>
<h2>{this.props.num}</h2>
</div>
)
}
}
设置子组件的某个props属性必须在使用的时候给其传值
只需要在子组件内设置propTypes的时候加上isRequired即可
import React, { Component } from 'react'
import PropTypes from 'prop-types'
export default class Sub extends Component {
static propTypes = {
// num: PropTypes.number.isRequired //设置为isRequired表示必须传值
num: PropTypes.number.isRequired
}
render() {
return (
<div>
<h2>{this.props.num}</h2>
</div>
)
}
}
设置默认值
如果直接在propTypes内对属性设置初始默认值,则以该值为默认值的同时限制该值的类型为默认值的类型
传值的时候简写
- 由于我们之前通过父组件将多个属性传给子组件的时候需要一个一个调用去传值
- 为了简写,React支持ES6的简写的方式去传值
- 也就是说,当给子组件传的值中属性名与父组件的相同的情况下,我们可以将这些个属性在父组件中使用Object的形式封装起来然后直接在标签内设置{…obj},react就会将该Object中属性名与子组件相同的属性对应进行解构赋值。
父组件
import React, { Component } from 'react'
import Chile from '../PropsCom/Chile.js'
export default class Father extends Component {
render() {
var Obj={
a:'FatherA',
b:'FatherB'
}
return (
<div>
<Chile num={123} name='chile' {...Obj} />
</div>
)
}
}
子组件
import React, { Component } from 'react'
import CheckPropTypes from 'prop-types'
export default class Chile extends Component {
state={
propx:this.props
}
render() {
return (
<div>
num={this.props.num}
<h1>name={this.props.name}</h1>
obj.a={this.props.a}
obj.b={this.props.b}
</div>
)
}
}
函数式组件内的Props
在函数式组件内实现通过props将父组件的数据传递到子组件,只需要在函数式的函数的形参列表上定义一个参数用于接收父组件传递过来的prop即可,React会把所有 传递过来的prop都封装到指定的形式参数中
import React from 'react'
//通过props去接收父组件传递过来的props,会将所有传进来的prop都放在props里面
export default function FunChile(props) {
let ThisProps=props
console.log(ThisProps);
return (
<div>
this is FunChile
<br/>
{ThisProps.bg}
</div>
)
}
Props VS state
相同点
两者都是纯JS对象,都会触发render更新,都具有确定性
不同点
- 属性可以从父组件获取,状态不能
- 属性可以有父组件修改,状态不能
- 属性和状态都可以在内部设置默认值,但是方式不同
- 属性不在组件内部进行修改,状态得在组件内部进行修改
- 属性可以设置子组件初始值,状态不能
- 属性可以修改子组件的值,状态不能
state
- state的主要作用是用于组件保存、控制、修改组件的可变状态,state在组件内部初始化
- state可以被组件自身修改,而外部不能访问也不能修改
- state可以被认为是一个局部的,只能被组件自身控制的数据源
- state中状态可以通过this.setState方法进行更新,setState会导致组件的重新渲染
Props
- props的主要作用是让使用组件的父组件可以传入参数来配置该组件。
- props是外部传进来的配置参数
- 组件内部无法控制也无法修改
- 除非父组件重新传入新的props,不然组件的props是不变的
表单的受控与不受控
React中的表单组件,分为2类
- 不受控组件(和状态无关) 1. 在input标签组件中,使用ref属性,来代表组件标识2. 组件渲染后,会自动把有ref属性的dom对象,挂到this.refs上 this.refs = { ref名1 : dom对象 ref名2 : dom对象 }3. 在需要的场景(一般指回调),使用this.refs来获取需要的对象,然后再做dom操作
- 受控组件(和状态紧密相连) 1. 初始化状态2. 在input标签的value属性上,绑定状态(输入框的默认值和状态有关)3. 在input标签上维护onChange属性,触发时即时修改状态4. 自动:当状态修改,立即触发声明周期的render方法,显示最先的状态值
使用: 如果对值的控制度不高,直接不受控组件 如果要严格把控值的操作,受控可以做表单验证等严格的逻辑(推荐)
版权归原作者 Moden-cray 所有, 如有侵权,请联系我们删除。