优秀的判断力来自经验,但经验来自于错误的判断。 –Fred Brooks
React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设Instagram 的网站。做出来以后,发现这套东西很好用,就在2013年5月开源了。
轻量级的视图层库!A JavaScript library for building user interfaces
React不是一个完整的MVC框架,最多可以认为是MVC中的V(View),甚至React并不非常认可MVC开发模式;React 构建页面 UI 的库。可以简单地理解为,React 将界面分成了各个独立的小块,每一个块就是组件,这些组件之间可以组合、嵌套,就成了我们的页面。
示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。
全局安装create-react-app
$ npm install -g create-react-app
创建一个项目
$ create-react-app your-app 注意命名方式
Creating a new React app in /dir/your-app.
Installing packages. This might take a couple of minutes. 安装过程较慢,
Installing react, react-dom, and react-scripts...
如果不想全局安装,可以直接使用npx
$ npx create-react-app myapp 也可以实现相同的效果
这需要等待一段时间,这个过程实际上会安装三个东西
出现下面的界面,表示创建项目成功:
Success! Created your-app at /dir/your-app
Inside that directory, you can run several commands:
npm start
Starts the development server.
npm run build
Bundles the app into static files for production.
npm test
Starts the test runner.
npm run eject
Removes this tool and copies build dependencies, configuration files and scripts into the app directory. If you do this, you can’t go back!
We suggest that you begin by typing:
cd your-app npm start
Happy hacking!
根据上面的提示,通过cd your-app 命令进入目录并运行npm start 即可运行项目。生成项目的目录结构如下:
├── README.md 使用方法的文档
├── node_modules 所有的依赖安装的目录
├── package-lock.json 锁定安装时的包的版本号,保证团队的依赖能保证一致。
├── package.json
├── public 静态公共目录
└── src 开发用的源代码目录
常见问题:
npm安装失败
react开发需要引入多个依赖文件:react.js、react-dom.js,分别又有开发版本和生产版本,create-react-app里已经帮我们把这些东西都安装好了。把通过CRA创建的工程目录下的src目录清空,然后在里面重新创建一个index.js. 写入以下代码:
// 从 react 的包当中引入了 React。只要你要写 React.js 组件就必须引入React, 因为react里有一种语法叫JSX,稍后会讲到JSX,要写JSX,就必须引入React
import React from 'react'
// ReactDOM 可以帮助我们把 React 组件渲染到页面上去,没有其它的作用了。它是从 react-dom 中引入的,而不是从 react 引入。
import ReactDOM from 'react-dom'
// ReactDOM里有一个render方法,功能就是把组件渲染并且构造 DOM 树,然后插入到页面上某个特定的元素上
ReactDOM.render(
// 这里就比较奇怪了,它并不是一个字符串,看起来像是纯 HTML 代码写在 JavaScript 代码里面。语法错误吗?这并不是合法的 JavaScript 代码, “在 JavaScript 写的标签的”语法叫 JSX-JavaScript XML。
<h1>欢迎进入React的世界</h1>,
// 渲染到哪里
document.getElementById('root')
)
注意:
目前有助于:
识别不安全的生命周期
关于使用过时字符串 ref API 的警告
检测意外的副作用
检测过时的 context API
安装 React DevTools 之后,右键点击页面的任何一个元素,然后选择“查看”,这样就能打开浏览器的开发者工具了,并且工具栏最后会多展示一个 React 的选项卡(包含 “⚛️ Components” 和 “⚛️ Profiler”)。你可以使用 “⚛️ Components” 来检查组件树。
不过,如果你使用的是 CodePen 在线编辑器的话,还需要几步操作才能正确使用开发工具
登录或注册,然后在邮件中确认(需要关闭垃圾邮件)。
点击 “Fork” 按钮。
点击 “Change View”,然后选择 “Debug mode”。
上一步会打开一个新的标签页,此时开发者工具就会有一个 React 标签了。
JSX 将 HTML 语法直接加入到 JavaScript 代码中,再通过翻译器转换到纯 JavaScript 后由浏览器执行。在实际开发中,JSX 在产品打包阶段都已经编译成纯 JavaScript,不会带来任何副作用,反而会让代码更加直观并易于维护。 编译过程由Babel 的 JSX 编译器实现。
https://reactjs.org/docs/hello-world.html
原理:
要明白JSX的原理,需要先明白如何用 JavaScript 对象来表现一个 DOM 元素的结构?看下面的DOM结构
<div class='app' id='appRoot'>
<h1 class='title'>欢迎进入React的世界</h1>
<p>
React.js 是一个帮助你构建页面 UI 的库
</p>
</div>
上面这个 HTML 所有的信息我们都可以用 JavaScript 对象来表示:
{
tag: 'div',
attrs: { className: 'app', id: 'appRoot'}, children: [
{
tag: 'h1',
attrs: { className: 'title' }, children: [' 欢迎进入React的世界 ']
},
{
tag: 'p', attrs: null,
children: ['React.js 是一个构建页面 UI 的库']
}
]
}
但是用 JavaScript 写起来太长了,结构看起来又不清晰,用 HTML 的方式写起来就方便很多了。
于是 React.js 就把 JavaScript 的语法扩展了一下,让 JavaScript 语言能够支持这种直接在 JavaScript 代码里面编写类似 HTML 标签结构的语法,这样写起来就方便很多了。编译的过程会把类似 HTML 的 JSX结构转换成 JavaScript 的对象结构。
下面代码:
import React from 'react';
import ReactDOM from 'react-dom';
class App extends React.Component { render () { return (
<div className='app' id='appRoot'>
<h1 className='title'>欢迎进入React 的世界</h1>
<p>
React.js 是一个构建页面 UI 的库
</p>
</div>
) } } ReactDOM.render(
<App />, document.getElementById('root') )
编译之后将得到这样的代码:
import React from 'react'
import ReactDOM from 'react-dom'
class App extends React.Component { render () {
return ( React.createElement(
"div"
{
className: 'app', id: 'appRoot'
},
React.createElement( "h1",
{ className: 'title' }, "欢迎进入React 的世界"
),
React.createElement( "p",
null,
"React.js 是一个构建页面 UI 的库"
)
)
)
}
}
ReactDOM.render( React.createElement(App),
document.getElementById('root')
)
React.createElement
会构建一个 JavaScript 对象来描述你 HTML 结构的信息,包括标签名、属性、
还有子元素等, 语法为
React.createElement( type,[props], [...children])
所谓的 JSX 其实就是 JavaScript 对象,所以使用 React 和 JSX 的时候一定要经过编译的过程:
JSX —使用react构造组件,bable进行编译—> JavaScript对象 — ReactDOM.render() —>DOM元素 —>插入页面
ES6的加入让JavaScript直接支持使用class来定义一个类,react创建组件的方式就是使用的类的继承,
ES6 class 是目前官方推荐的使用方式,它使用了ES6标准语法来构建,看以下代码:
import React from 'react'
import ReactDOM from 'react-dom'
class App extends React.Component {
render () {
return (<h1>欢迎进入React的世界</h1>)
}
}
ReactDOM.render(<App />, document.getElementById('root'))
es6 class 组件其实就是一个构造器,每次使用组件都相当于在实例化组件,像这样:
import React from 'react'
import ReactDOM from 'react-dom'
class App extends React.Component {
render () {
return (<h1>欢迎进入 {
this.props.name
}
的世界 </h1>)
}
}
const app=new App( {
name: 'react'
}
).render()
ReactDOM.render(app,
document.getElementById('root'))
import React from 'react'
import ReactDOM from 'react-dom'
const App = (props) => <h1> 欢迎进入React的世界</h1> ReactDOM.render(
// React组件的调用方式
<App />, document.getElementById('root')
)
这样一个完整的函数式组件就定义好了。但要注意!注意!注意!组件名必须大写,否则报错。
// 注意这里的两个括号,第一个表示我们在要JSX里插入JS了,第二个是对象的括号
<p style={{color:'red', fontSize: '14px'}}>Hello world</p>
行内样式需要写入一个样式对象,而这个样式对象的位置可以放在很多地方,例如render 函数里、组件原型上、外链js文件中
<p className="hello">Hello world</p>
注意:
class ==> className , for ==> htmlFor(label)
采用on+事件名的方式来绑定一个事件,注意,这里和原生的事件是有区别的,原生的事件全是小写 onclick , React里的事件是驼峰 onClick ,React的事件并不是原生事件,而是合成事件。
import React, { Component } from 'react'
export default class App extends Component {
a = 100
render() {
return (
<div>
<input />
<button onClick={ ()=>{
console.log("click1","如果处理逻辑过多, 不推荐这种写法",this.a)
} }>add1</button>
<button onClick={ this.handleClick2.bind(this) }>add2-不推荐这种写法</button>
<button onClick={ this.handleClick3 }>add3-推荐</button>
<button onClick={ ()=>this.handleClick4() }>add4-比较推荐-传参</button>
</div>
)
}
handleClick2(){
console.log("click2",this.a)
}
handleClick3 = (evt)=>{
console.log("click3",this.a,evt.target)
}
handleClick4(){
console.log("click4",this.a)
}
}
/*
React并不会真正的绑定事件到每一个具体《》的元素上,而是采用事件代理的模式:
*/
/*
call, 改变this, 自动执行函数
apply,改变this, 自动执行函数
bind, 改变this, 不会自动执行函数,手动加括号执行函数
*/
var obj1 = {
name:"obj1",
getName(){
console.log(this.name)
}
}
var obj2 = {
name:"obj2",
getName(){
console.log(this.name)
}
}
obj1.getName.bind(obj2)()
// obj2.getName()
直接在render里写行内的箭头函数(不推荐)在组件内使用箭头函数定义一个方法(推荐)
直接在组件内定义一个非箭头函数的方法,然后在render里直接使用 onClick =
{this.handleClick.bind(this)} (不推荐)
直接在组件内定义一个非箭头函数的方法,然后在constructor里bind(this)(推荐)
Event对象和普通浏览器一样,事件hander会被自动传入一个event,event 对象,这个对象和普通的浏览器 对象所包含的方法和属性都基本一致。不同的是 React中的 event 对象并不是浏览器提供的,而是它自己内部所构建的。它同样具有event.stopPropagation 、 event.preventDefault 这种常用的方法
给标签设置ref=“username” 通过这个获取this.refs.username , ref可以获取到应用的真实dom
给组件设置ref=“username” 通过这个获取this.refs.username ,ref可以获取到组件对象
import React, { Component } from 'react'
export default class App extends Component {
a = 100
myref = React.createRef()
render() {
return (
<div>
{/* */}
<input ref={this.myref}/>
<button onClick={ ()=>{
// console.log("click1",this.refs.mytext.value)
console.log("click",this.myref.current.value)
} }>add1</button>
<button onClick={this.handleClick2}>add2</button>
</div>
)
}
handleClick2 = ()=>{
console.log("click2",this.myref.current.value)
}
}
myRef = React.createRef()
<div ref={this.myRef}>hello</div>
访问this.myRef.current
状态就是组件描述某种显示情况的数据,由组件自己设置和更改,也就是说由组件自己维护,使用状态的目的就是为了在不同的状态下使组件的显示不同(自己管理)
this.state 是纯js对象,在vue中,data属性是利用Object.defineProperty处理过的,更改data的数据的时候会触发数据的 getter 和setter ,但是React中没有做这样的处理,如果直接更改的话,react是无法得知的,所以,需要使用特殊的更改状态的方法 setState。
state = {
mytext:"收藏",
myShow:true
}
constructor(){
super()
this.state = {
mytext:"收藏",
myShow:true,
}
}
例:
import React, { Component } from 'react'
export default class App extends Component {
a = 1
// state = {
// mytext:"收藏",
// myShow:true
//}
constructor(){
super()
this.state = {
mytext:"收藏",
myShow:true,
myname:"kerwin"
}
}
render() {
// var text="收藏"
return (
<div>
<h1>欢迎来到react开发-{this.state.myname}</h1>
<button onClick={()=>{
// this.state.mytext = "取消" //不用直接修改状态
this.setState({
// mytext:"取消收藏"
myShow:!this.state.myShow,
myname:"xiaoming"
}) // 间接修改状态
if(this.state.myShow){
console.log("收藏的逻辑")
}else{
console.log("取消收藏的逻辑")
}
}}>{this.state.myShow?'收藏':"取消收藏"}</button>
</div>
)
}
}
this.setState({
// mytext:"取消收藏"
myShow:!this.state.myShow,
myname:"xiaoming"
}) // 间接修改状态
setState 有两个参数
参数是对象
this.setState({
isLiked: !this.state.isLiked
})
参数是方法
this.setState((prevState, props) => { return {
isLiked: !prevState.isLiked
}
})
注意的是这个方法接收两个参数,第一个是上一次的state, 第二个是props
2. 因为setState是异步的,相当于不是实时更新数据,所以想要获取最新的state 就需要的第二个参数,是一个可选择的回调参数
this.setState((prevState, props) => { return {
isLiked: !prevState.isLiked
}
}, () => {
console.log('回调里的 ',this.state.isLiked)
})
console.log('setState外部的 ',this.state.isLiked)
循环渲染 为了列表的复用和重排, 设置key值, 提高性能; 理想key ,item.id 不涉及到列表的增加删除 ,重排,
设置成索引没有问题。
//状态
state = {
list:[{id:1,text:"1111"},
{id:2,text:"2222"},
{id:3,text:"3333"}]
}
// 使用map渲染
<div><ul>{
this.state.list.map(
(item,index)=>
<li key={index}>{item.text}--{index}</li>
)
}</ul></div>
原生js写法
/*
原生js - map
*/
var list= ["aa","bb","cc"]
var newlist = list.map(item=>`${item}`)
console.log(newlist.join(""))
import React, { Component } from 'react'
export default class App extends Component {
state ={ count:1 }
render() {
return (
<div>
{this.state.count}
<button onClick={this.handleAdd1}>add1</button>
<button onClick={this.handleAdd2}>add2</button>
</div>
)
}
handleAdd1 = ()=>{
this.setState({
count:this.state.count+1
},()=>{
console.log(this.state.count)
})
this.setState({
count:this.state.count+1
},()=>{
console.log(this.state.count)
})
this.setState({
count:this.state.count+1
},()=>{
console.log(this.state.count)
// 状态 和真实dom已经更新完了,
})
}
handleAdd2 = ()=>{
setTimeout(()=>{
this.setState({
count:this.state.count+1
})
console.log(this.state.count)
this.setState({
count:this.state.count+1
})
console.log(this.state.count)
this.setState({
count:this.state.count+1
})
console.log(this.state.count)
},0)
}
}
链接: BetterScroll
BetterScroll 是一款重点解决移动端(已支持 PC)各种滚动场景需求的插件。它的核心是借鉴的 iscroll (opens new
window)的实现,它的 API 设计基本兼容 iscroll,在 iscroll 的基础上又扩展了一些 feature
以及做了一些性能优化。 BetterScroll 是使用纯 JavaScript 实现的,这意味着它是无依赖的。
引入依赖
import BetterScroll from 'better-scroll'
使用方法
直接绑定组件dom节点的className
import React, { Component } from 'react'
import BetterScroll from 'better-scroll'
export default class App extends Component {
state = {list:[]}
render() {
return (
<div>
<button onClick={()=>this.getData()}>click</button>
<div className="kerwinwrapper" style={{height:'200px',background:'yellow',overflow:'hidden'}}>
<ul className="kerwincontent">
{
this.state.list.map(item=>
<li key={item}>{item}</li>
)
}
</ul>
</div>
</div>
)
}
getData(){
var list =[1,2,3,4,5,6,7,8,9,0,11,12,13,14,15]
// this.setState({
// list:list
// },()=>{
// console.log(this.state.list)
// console.log(document.querySelectorAll("li"))
// new BetterScroll(".kerwinwrapper")
// }
证明setState 为异步调用
setTimeout(()=>{
this.setState({list:list })
console.log(this.state.list)
console.log(document.querySelectorAll("li"))
new BetterScroll(".kerwinwrapper")
},0)
}
}
props是正常是外部传入的,组件内部也可以通过一些方式来初始化的设置,属性不能被组件自己更改,但是你可以通过父组件主动重新渲染的方式来传入新的props属性是描述性质、特点的,组件自己不能随意更改。
之前的组件代码里面有props 的简单使用,总的来说,在使用一个组件的时候,可以把参数放在标签的
属性当中,所有的属性都会作为组件参数来接收props : 对象的键值。通过箭头函数创建的组件,需要通过函数的
state = {//只能内部自己用的,外面无法改变。}
//父组件定义
var obj = {title:"测试",leftshow:false }
<Navbar title={obj.title} leftshow={obj.leftshow} />
<Navbar {...obj} />
//子组件接受
//属性是父组件传来的,this.props
let {title,leftshow} = this.props
// 类属性
import propTypes from 'prop-types' 引入校验方法
static propTypes = {
title:propTypes .string, //做校验
leftshow:propTypes .bool //做校验
}
//默认值
static defaultProps = {
leftshow:true
}
相似点:
不同点:
state 的主要作用是用于组件保存、控制、修改自己的可变状态。 state
在组件内部初始化,可以被组件自身修改,而外部不能访问也不能修改。你可以认为 state 是一个局部的、只能被组件自身控制的数据源。
state 中状态可以通过 this.setState 方法进行更新, setState 会导致组件的重新渲染。 props
的主要作用是让使用该组件的父组件可以传入参数来配置该组件。它是外部传进来的配置参数,组件内部无法控制也无法修改。非外部组件主动传入新的
props ,否则组件的 props 永远保持不变。没有 state 的组件叫无状态组件(stateless component),设置了 state 的叫做有状态组件(stateful
component)。因为状态会带来管理的复杂性,我们尽量多地写无状态组件,尽量少地写有状态的组件。这样会降低代码维护的难度,也会在一定程度上增强组件的可复性。
通俗的讲,状态就是自己内在的心情,可以自己控制调节,而属性就像是父母给的外貌与性别,不能随意更改。
父子之间信息传递,属于单向数据流
父——》子,子想更改,子——》父告诉父想更改,再父——》子
import React, { Component } from 'react'
class Child extends Component{
render(){
return <div>
child-{this.props.text}
<button onClick={()=>{
this.props.text = "333333333333333333"
}}>click-子</button>
</div>
}
}
export default class App extends Component {
state = {text:"111111111"}
render() {
return (
<div>
<button onClick={()=>{
this.setState({
text:"2222222"
})
}}>click-父</button>
<Child text = {this.state.text}/>
</div>
)
}
}
{condition ? '渲染列表的代码' : '空空如也'}
// 数据
const people = [{ id: 1,name: 'Leo', age: 35}, {id: 2,name: 'XiaoMing', age: 16}]
// 渲染列表
{
people.map(person => { return
(
<dl key={person.id}>
<dt>{person.name}</dt>
<dd>age: {person.age}</dd>
</dl>
)
})
}
React的高效依赖于所谓的 Virtual-DOM,尽量不碰 DOM。对于列表元素来说会有一个问题:元素可能会在一个列表中改变位置。要实现这个操作,只需要交换一下 DOM 位置就行了,但是React并不知道 其实我们只是改变了元素的位置,所以它会重新渲染后面两个元素(再执行 Virtual-DOM ),这样会大 大增加 DOM 操作。但如果给每个元素加上唯一的标识,React 就可以知道这两个元素只是交换了位 置,这个标识就是 key ,这个 key 必须是每个元素唯一的标识。
对于富文本创建的内容,后台拿到的数据是这样的:
content = "React.js是一个构建UI 的库
"
处于安全的原因,React当中所有表达式的内容会被转义,如果直接输入,标签会被当成文本。这时候就需要使用dangerouslySetHTML 属性,它允许我们动态设置innerHTML
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
class App extends Component {
constructor() {
super() this.state = {content : "React.js是一个构建UI 的库
"}
}
render () {
return (
<div
// 注意这里是两个下下划线 html
dangerouslySetInnerHTML={{ __html: this.state.content}}
/>
)
}
}
ReactDOM.render(
<App/>,
document.getElementById('root')
)
React要编写一个非受控组件,可以 使用 ref 来从 DOM 节点中获取表单数据,就是非受控组件。
例如,下面的代码使用非受控组件接受一个表单的值:
import React, { Component } from 'react'
export default class App extends Component {
myusername = React.createRef() //ref绑定到input组件上
render() {
return (
<div>
<h1>登录页</h1>
<input type="text" ref={this.myusername} defaultValue="kerwin"/>
<button onClick={()=>{
console.log(this.myusername.current.value)
}}>登录</button>
<button onClick={()=>{
this.myusername.current.value = ""
}}>重置</button>
{/* */ }
</div>
)
}
}
?为什么input不使用value绑定数据,这里可以把input当成一个React的封装组件,value值相当于子组件的属性,属性由子修改,所以只能更新一次。
因为非受控组件将真实数据储存在 DOM 节点中,所以在使用非受控组件时,有时候反而更容易同时集成 React 和非 React代码。如果你不介意代码美观性,并且希望快速编写代码,使用非受控组件往往可以减少你的代码量。否则,你应该使用受控组件。
默认值
在 React 渲染生命周期时,表单元素上的 value 将会覆盖 DOM 节点中的值,在非受控组件中,你经常希望 React 能赋予组件一个初始值,但是不去控制后续的更新。 在这种情况下, 你可以指定一个defaultValue 属性,而不是 value 。
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input
defaultValue="Bob"
type="text"
ref={this.input} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
同样, <input type="checkbox"> 和 <input type="radio"> 支持 defaultChecked , <select>
和 <textarea> 支持 defaultValue 。
import React, { Component } from 'react'
export default class App extends Component {
// myusername = React.createRef()
// 这样的写法也是声明在实例上的对象
state = {username:"name"}// 给组件状态设置默认值,在实时修改时进行校验
render() {
return (
<div>
<h1>登录页</h1>
// evt为原生的事件绑定对象
<input type="text" value={this.state.username} onChange={(evt)=>{
console.log("onChange",evt.target.value)
this.setState({
username:evt.target.value // 获取原生对象上的属性并更新
})
}}/>
<button onClick={()=>{
console.log(this.state.username)
}}>登录</button>
<button onClick={()=>{
this.setState({
username:""
})
}}>重置</button>
{/* */ }
</div>
)
}
}
受控组件更新state的流程:
于在表单元素上设置了 value 属性,因此显示的值将始终为 this.state.value ,这使得 React 的 state
成为唯一数据源。由于 handlechange 在每次按键时都会执行并更新 React 的 state,因此显示的值将随着用户输入而更新。对于受控组件来说,输入的值始终由 React 的 state 驱动。你也可以将 value 传递给其他 UI
元素,或者通过其他事件处理函数重置,但这意味着你需要编写更多的代码。
注意: 另一种说法(广义范围的说法),React组件的数据渲染是否被调用者传递的 props 完全控制,控制则为受控组件,否则非受控组件。
数据通信是单向的,数据必须是由一方到另一方。
代码示例:
import React, { Component } from 'react'
class Navbar extends Component{
render(){
return <div style={{background:"red"}}>
<button onClick={()=>{
console.log("子通知父, 让父的isSHow 取反。",this.props.event)
this.props.event() //调用父组件传来啊的回调函数
}}>click</button>
<span>navbar</span>
</div>
}
}
class Sidebar extends Component{
render(){
return <div style={{background:"yellow",width:"200px"}}>
<ul>
<li>11111</li>
<li>11111</li>
<li>11111</li>
</ul>
</div>
}
}
export default class App extends Component {
state = {
isShow:false
}
handleEvent = ()=>{
this.setState({
isShow:!this.state.isShow
})
// console.log("父组件定义的event事件")
}
render() {
return (
<div>
<Navbar event={this.handleEvent}/>
{/* */}
{this.state.isShow && <Sidebar/>}
</div>
)
}
}
父子通信版表单域组件
import React, { Component } from 'react'
class Field extends Component{
render(){
return <div style={{background:"yellow"}}>
<label>{this.props.label}</label>
<input type={this.props.type} onChange={(evt)=>{
// console.log(evt.target.value)
this.props.onChangeEvent(evt.target.value)
}} value={this.props.value}/>
</div>
}
}
export default class App extends Component {
state = {
username:localStorage.getItem("username"),
password:""
}
render() {
return (
<div>
<h1>登录页面</h1>
<Field label="用户名" type="text" onChangeEvent={(value)=>{
// console.log(value)
this.setState({
username:value
})
}} value={this.state.username}/>
<Field label="密码" type="password" onChangeEvent={(value)=>{
this.setState({
password:value
})
}} value={this.state.password}/>
<button onClick={()=>{
console.log(this.state.username,this.state.password,'发送后端验证')
}}>登录</button>
<button onClick={()=>{
this.setState({
username:"",
password:""
})
}}>取消</button>
</div>
)
}
}
import React, { Component } from 'react'
class Field extends Component{
state = {
value:""
}
clear(){
this.setState({
value:""
})
}
setValue(value){
this.setState({
value:value
})
}
render(){
return <div style={{background:"yellow"}}>
<label>{this.props.label}</label>
<input type={this.props.type} onChange={(evt)=>{
this.setState({
value:evt.target.value
})
}} value={this.state.value}/>
</div>
}
}
export default class App extends Component {
username = React.createRef()
password = React.createRef()
render() {
return (
<div>
<h1>登录页面</h1>
<Field label="用户名" type="text" ref={this.username}/>
<Field label="密码" type="password" ref={this.password}/>
<button onClick={()=>{
console.log(this.username.current.state.value,
this.password.current.state.value
)
}}>登录</button>
<button onClick={()=>{
this.username.current.clear()
this.password.current.clear()
}}>取消</button>
</div>
)
}
}
父组件拿到子组件的引用,从而调用子组件的方法。
React中的状态提升概括来说,就是将多个组件需要共享的状态提升到它们最近的父组件上.在父组件上改变这个状态然后通过props分发给子组件。
import React, { Component } from 'react'
export default class APP extends Component {
state = {
title: ''
}
render() {
return (
<div>
<One childTitle={this.state.title} onEvent={(e) => {
this.setState({
title: e
})
}} />
<Two twoTitle={this.state.title} />
</div>
)
}
}
//One组件向Two组件传信息
class One extends Component {
state = {
oneTitle: "rain"
}
render() {
return (
<div>
<button onClick={() => {
this.props.onEvent(this.state.oneTitle)
}}>点击</button>
</div>
)
}
}
class Two extends Component {
render() {
return (
<div style={{ backgroundColor: "yellow", width: '100px', height: "100px" }}>{this.props.twoTitle}</div>
)
}
}
subsrcibe()存入函数到list数组等待运行,调用publish()函数运行list数组中全部函数。
下面的代码中,仅在Two组件的constructor()函数中运行了一次subsrcibe()函数,所以list数组中仅有一个函数,每次运行publish()函数时,运行(text) => { this.setState({ info: text }) }
import React, { Component } from 'react'
// 调度中心
var obj = {
list: [],
// 每次调用此函数,存入一个函数到list
subsrcibe(callback) { //生产者
// console.log(callback)
this.list.push(callback)
},
// 每次调用此函数,运行list数组里的全部函数
publish(text) { //消费者
console.log(text)
// 遍历所有的list,将回调函数执行
this.list.forEach(callback => {
callback && callback(text)
})
}
}
export default class APP extends Component {
state = {
list: [1, 2, 3, 4, 5]
}
render() {
return (
<div>
{
this.state.list.map(item => <One key={item} msg={item} ></One>)
}
<Two />
</div>
)
}
}
class One extends Component {
render() {
return (
<div style={{ backgroundColor: "green", height: "20px", width: "20px" }} onClick={() => {
obj.publish(this.props.msg)
}
}>
<div>{this.props.msg}</div>
</div >
)
}
}
class Two extends Component {
constructor() {
super()
obj.subsrcibe((text) => {
this.setState({
info: text
})
})
}
state = {
info: ''
}
render() {
return (
<div style={{ backgroundColor: 'yellow', width: '100px', height: '100px' }}>
{this.state.info}
</div>
)
}
}
import React, { Component } from 'react'
const GlobalContext = React.createContext() //创建context对象
export default class Tewlfth extends Component {
state = {
user: 'rain',
sex: true
}
render() {
return (
<GlobalContext.Provider value={{ //生产者
user: this.state.user,
sex: this.state.sex,
changeUser: (value) => {
this.setState({
sex: !value
})
}
}}>
<div>
<One />
<Two />
</div>
</GlobalContext.Provider>
)
}
}
class One extends Component {
render() {
return (
<GlobalContext.Consumer> //消费者
{
(value) => {
return (
<div style={{ backgroundColor: "green", height: "100px", width: "100px" }} >
<div>{value.user}</div>
<button onClick={() => {
value.changeUser(value.sex)
}}>点击</button>
</div >
)
}
}
</GlobalContext.Consumer >
)
}
}
class Two extends Component {
render() {
return (
<GlobalContext.Consumer> //消费者
{
(value) => {
return (
<div style={{ backgroundColor: "yellow", width: "100px", height: '100px' }}>{value.sex ? "男" : "女"}</div>
)
}
}
</GlobalContext.Consumer>
)
}
}
注意:GlobalContext.Consumer内必须是回调函数,通过context方法改变根组件状态
context优缺点:
Portals 提供了一种很好的方法,将子节点渲染到父组件 DOM 层次结构之外的 DOM 节点。
jsx中的所有内容都会通过children prop传递到父组件中,使用react组合的方式可以实现类似于Vue插槽的功能;
示例:
import React, { Component } from 'react'
class Child extends Component{
render(){
return <div>
child
{/* 插槽 vue slot,具名插槽 */}
{this.props.children[2]} //父组件中有多个dom节点时,会被渲染成数组
{this.props.children[1]}
{this.props.children[0]}
</div>
}
}
class Swiper extends Component{
render(){
return <div>
{this.props.children}
</div>
}
}
export default class App extends Component {
render() {
return (
<div>
<Swiper>
<div>111111</div>
<div>222222</div>
<div>333333</div>
</Swiper>
<Swiper>
{/* 111111
222222
333333 */}
</Swiper>
<Swiper>
{/*
*/}
</Swiper>
<Child>
<div>11111111</div>
<div>22222222</div>
<div>33333333</div>
{
// children
}
</Child>
</div>
)
}
}
优点:
组件的生命周期可分成三个状态:
当组件实例被创建并插入 DOM 中时,其生命周期调用顺序如下:
- constructor(): 在 React 组件挂载之前,会调用它的构造函数。
- getDerivedStateFromProps(): 在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。
- render(): render() 方法是 class 组件中唯一必须实现的方法。
- componentDidMount(): 在组件挂载后(插入 DOM 树中)立即调用。
render() 方法是 class 组件中唯一必须实现的方法,其他方法可以根据自己的需要来实现。
import React, { Component } from 'react'
export default class App extends Component {
state = {
myname:"name"
}
UNSAFE_componentWillMount(){ //新版本已经被遗弃,加上UNSAFE表示不安全的,谨慎使用
console.log("第一次will mount",this.state.myname,document.getElementById("myname"))
// 第一次初始化的 最后一次修改状态机会
this.setState({
myname:"name"
})
//初始化数据的作用。
}
componentDidMount(){
console.log("第一次did mount",document.getElementById("myname"))
// 数据请求axios
// 订阅函数调用
// setInterval
// 基于创建的完的dom进行 初始化,,,,,,BetterScroll
}
render() {
console.log("render")
return (
<div>
<span id="myname">{this.state.myname}</span>
</div>
)
}
}
每当组件的 state 或 props 发生变化时,组件就会更新。
当组件的 props 或 state 发生变化时会触发更新。组件更新的生命周期调用顺序如下:
- getDerivedStateFromProps(): 在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。根据shouldComponentUpdate() 的返回值,判断 React组件的输出是否受当前 state 或 props 更改的影响。
- shouldComponentUpdate():当 props 或 state发生变化时,shouldComponentUpdate() 会在渲染执行之前被调用。
- render(): render() 方法是 class 组件中唯一必须实现的方法。
- getSnapshotBeforeUpdate(): 在最近一次渲染输出(提交到 DOM 节点)之前调用。
- componentDidUpdate(): 在更新后会被立即调用。
render() 方法是 class 组件中唯一必须实现的方法,其他方法可以根据自己的需要来实现。
例1:
import React, { Component } from 'react'
export default class App extends Component {
state = {
myname:"name"
}
render() {
console.log("render")
return (
<div>
<button onClick={()=>{
// this.state.myname = "xiaoming"
this.setState({
myname:"xiaoming"
})
}}>click</button>
{this.state.myname}
</div>
)
}
// scu 性能优化函数
shouldComponentUpdate(nextProps,nextState){
// return true; //应该更新
//return false;; //阻止更新
// this.state 老的状态
// nextState 新的状态
if(JSON.stringify(this.state)!== JSON.stringify(nextState)){
return true
}
return false //阻止之后不进行之后的渲染
}
UNSAFE_componentWillUpdate(){ //将要更新前的数据操作
console.log("UNSAFE_componentWillUpdate")
}
componentDidUpdate(prevProps,prevState){//已经更新后的数据操作 // 更新后, 想要获取dom节点, 更新
//prevState 老状态
console.log("componentDidUpdate")
//new BetterScroll("#warpper") 比如BetterScroll操作
}
}
例2:componentWillReceiveProps,子组件中使用
import React, { Component } from 'react'
class Child extends Component{
state ={
title:""
}
render(){
return <div>child-{this.state.title}</div>
}
componentWillReceiveProps(nextProps){
console.log("componentWillReceiveProps",nextProps)
// 最先获得父组件传来的属性, 可以利用属性进行ajax或者逻辑处理。
// 把属性转化成孩子自己的状态。
this.setState({
title:nextProps.text+"name"
})
}
}
export default class App extends Component {
state = {
text:"11111111111"
}
render() {
return (
<div>
{
this.state.text
}
<button onClick={()=>{
this.setState({
text:"222222222222"
})
}}>click</button>
<Child text={this.state.text}/>
</div>
)
}
}
当组件从 DOM 中移除时会调用如下方法:
- componentWillUnmount(): 在组件卸载及销毁之前直接调用。
import React, { Component } from 'react'
export default class App extends Component {
state = {
isCreated:true
}
render() {
return (
<div>
<button onClick={()=>{
this.setState({
isCreated:!this.state.isCreated
})
}}>click</button>
{/* {this.state.isCreated? :""} */}
{this.state.isCreated && <Child/>}
</div>
)
}
}
class Child extends Component{
render(){
return <div>
child
</div>
}
componentDidMount() {
window.onresize = ()=>{
console.log("resize")
}
this.timer = setInterval(()=>{
console.log("111")
},1000)
}
componentWillUnmount(){ //不进行销毁,执行函数会一直进行下去,必须进行销毁操作
console.log("componentWillUnmount")
window.onresize = null
clearInterval(this.timer)
}
}
//老的生命周期的写法
componentDidMount() {
if(this.props.value!==undefined){ this.setState({
current:this.props.value
})
}
}
componentWillReceiveProps(nextProps){
if(nextProps.value !==undefined){
this.setState({ current:nextProps.value
})
}
}
// 新的生命周期写法
static getDerivedStateFromProps(nextProps) {
if(nextProps.value !==undefined){
return {
current:nextProps.value
}
}
return null
}
//新的数据不断插入数据前面, 导致我正在看的数据向下走,如何保持可视区依旧是我之前看的数据呢?
getSnapshotBeforeUpdate(){ //保留上一次的状态
return this.refs.wrapper.scrollHeight
}
componentDidUpdate(prevProps, prevState,preHeight) {
//if(preHeight===200)return ;
this.refs.wrapper.scrollTop += this.refs.wrapper.scrollHeight-preHeight
}
<div style={{height:"200px",overflow: "auto"}}} ref="wrapper">
<ul>
.........
</ul>
</div>
新生命周期例子1:
import React, { Component } from 'react'
export default class App extends Component {
state = {
myname:"name",
myage:100
}
// componentWillMount 初始化
static getDerivedStateFromProps(nextProps,nextState){ //属于静态方法 是类方法
// console.log(this)
console.log("getDrivedStateFromProps",nextState)
return {
myname:nextState.myname.substring(0,1).toUpperCase()+nextState.myname.substring(1)
}
}
render() {
return (
<div>
<button onClick={()=>{
this.setState({
myname:"xiaoming"
})
}}>click</button>
app - {this.state.myname}-{this.state.myage}
</div>
)
}
}
新生命周期例子2:
import React, { Component } from 'react'
export default class App extends Component {
state = {
mytext:"111111"
}
render() {
console.log("render")
return (
<div>
app
<button onClick={()=>{
this.setState({
mytext:"222222"
})
}}>click</button>
{this.state.mytext}
</div>
)
}
// componentWillUpdate(){
// console.log("componentWillUpdate")
// }
componentDidUpdate(prevProps,prevState,value){
console.log("componentDidUpdate",value)
}
getSnapshotBeforeUpdate(){
console.log("getSnapshotBeforeUpdate")
return 100;
}
}
控制组件自身或者子组件是否需要更新,尤其在子组件非常多的情况下, 需要进行优化。
PureComponent会帮你 比较新props 跟 旧的props, 新的state和老的state(值相等,或者对象含有相同的属性、且属性值相等 ),决定shouldcomponentUpdate 返回true 或者 false, 从而决定要不要呼叫 render function。
注意:
如果你的 state 或 props 『永远都会变』,那 PureComponent 并不会比较快,因为shallowEqual 也需要花时间。
例:
import React, { PureComponent } from 'react'
export default class App extends PureComponent {
state = {
myname:"kerwin"
}
render() {
console.log("render")
return (
<div>
<button onClick={()=>{
// this.state.myname = "xiaoming"
this.setState({
myname:"xiaoming"
})
}}>click</button>
{this.state.myname}
</div>
)
}
// scu 性能优化函数
// shouldComponentUpdate(nextProps,nextState){
// // return true; //应该更新
// //return false;; //阻止更新
// // this.state 老的状态
// // nextState 新的状态
// if(JSON.stringify(this.state)!== JSON.stringify(nextState)){
// return true
// }
// return false
// }
UNSAFE_componentWillUpdate(){
console.log("UNSAFE_componentWillUpdate")
}
componentDidUpdate(){
console.log("componentDidUpdate")
}
}
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
Hook 本质上就是一个函数,它简洁了组件,有自己的状态管理,生命周期管理,状态共享。
使用hooks的理由
什么时候使用 Hook ?
组件之间状态复用, 例如:使用 useContext 可以很好的解决状态复用问题,或者自定义 Hook来定制符合自己业务场景遇到的状态管理。
在函数组件中 生命周期的使用,更好的设计封装组件。在函数组件中是不能直接使用生命周期的,通过 Hook 很好的解决了此问题。
函数组件与 class 组件的差异,还要区分两种组件的使用场景。使用 Hook 完全不用去想这些,它可以使用更多 React新特性。
什么时候使用 Hook ?
React 内置的 Hook
useState 状态管理
useEffect 生命周期管理
useContext 共享状态数据
useMemo 缓存值
useRef 获取 Dom 操作
useCallback 缓存函数
useReducer redux 相似
useImperativeHandle 子组件暴露值 / 方法
useLayoutEffect 完成副作用操作,会阻塞浏览器绘制
在 class 组件中,我们获取 state 是 通过 this.state 来获取的。 而在函数组件中,是没有 this 的,
我们可以使用 Hook 提供的 useState 来管理和维护 state
const [state, setState] = useState(initialState)
1. setState 为更新 satate 方法
2. useState(initialState) initialState 为初始值
例子:
import {useState} from 'react';
export default () => {
const [data, setData] = useState('小明')
return (
<div>
<h1>{data}</h1>
{/* 更新 state */}
<button onClick={()=>{setData('小李')}}></button>
</div>
)
}
useEffect 可以看作是 函数式 组件 的 生命周期管理。 因为在 函数式组件中无法直接使用生命周期,就必须托管 Hook来进行管理使用了。
useEffect 可以使用的 3 个生命周期函数:
Function Component 不存在生命周期,所以不要把 Class Component 的生命周期概念搬过来试图对号入座。
useEffect(() => {
//effect return () => {
//cleanup
};
}, [依赖的状态;空数组,表示不依赖])
不要对 Dependencies 撒谎, 如果你明明使用了某个变量,却没有申明在依赖中,你等于向 React 撒了谎,后果就是,当依赖的变量改变时,useEffect 也不会再次执行, eslint会报警告
React 更新 DOM 之后运行一些额外的代码」
那么它就是在生命周期的 compoentDidmount 和 componentUpdate 中执行即可
useEffect(() => {
//默认会执行
// 这块相当于 class 组件 生命周期的
//compoentDidmount compoentDidUpdate
}, [])
当组件进行卸载时,需要执行某些事件处理时,就需要用到 class 组件生命周期的 componentUnmount .
在 useEffect 中很方便使用,在内部返回一个方法即可,在方法中写相应业务逻辑
useEffect返回返回函数解释
这是 effect 可选的清除机制。每个 effect 都可以返回一个清除函数。如此可以将添加和移除订阅的逻辑放在一起。它们都属于 effect 的一部分。
useEffect(()=>{
return () => {
console.log('组件卸载时执行')
}
})
可以通过控制 监听 state 变化来实现相应的业务逻辑。
useEffect(() => {
// 监听num,count 状态变化
// 不监听时为空 [] , 或者不写
}, [num, count])
例子:
import { useState, useEffect } from 'react';
export default () => {
const [num, setNum] = useState(0)
const [count, setCount] = useState(1)
useEffect(() => {
//默认会执行
// 这块相当于 class 组件 生命周期的 compoentDidmount compoentDidUpdate
console.log(`num: ${num}`)
console.log(`count: ${count}`)
// 组件在卸载时,将会执行 return 中内容
return () => {
// 相当于 class 组件生命周期的 componentWillUnMount
console.log('测试')
}
}, [num])
return (
<div>
<h1>{num}</h1>
<button onClick={() => { setNum(num + 1) }}> 更新Num</button>
<hr />
<h1>{count}</h1>
<button onClick={() => { setCount(count + 1) }}> 更新Count</button>
</div>
)
}
简单来说就是调用时机不同, useLayoutEffect 和原来 componentDidMount & componentDidUpdate 一致,在 react完成 DOM更新后马上同步调用的代码,会阻塞页面渲染。而 useEffect 是会在整个页面渲染完才会调用的代码。
官方建议优先使用useEffect
However, we recommend starting with useEffect first and only trying useLayoutEffect if that causes a problem.
在实际使用时如果想避免页面抖动(在useEffect 里修改DOM很有可能出现)的话,可以把需要操作DOM的代码放在useLayoutEffect 里。在这里做点dom操作,这些 dom修改会和 react 做出的更改一起被一次性渲染到屏幕上,只有一次回流、重绘的代价。
防止因为组件重新渲染,导致方法被重新创建 ,起到缓存作用; 只有第二个参数 变化了,才重新声明一次
例子:
import { useState, useCallback} from 'react';
export default () => {
const [count, setCount] = useState(0)
const [num, setNum] = useState(0)
const newValue = useCallback(()=>{
console.log(`count 值为${count}`)
console.log(`num 值为 ${num}`)
return count+num
},[count])
return(
<div>
<h1>{count}</h1>
<button onClick={()=>{setCount(count+1)}}>count + 1</button>
<hr/>
<h1>{num}</h1>
<button onClick={()=>{setNum(num+1)}}>Num + 1</button>
<hr/>
{/* 调用useCallback 返回的值 */}
<h2>{newValue()}</h2>
</div>
)
}
useCallback 的功能完全可以由 useMemo 所取代,如果你想通过使用 useMemo 返回一个记忆函数也是完全可以的。
唯一的区别是:useCallback 不会执行第一个参数函数,而是将它返回给你,而 useMemo 会执行第一个函数并且将函数执行结果返回给你。
所以 useCallback 常用记忆事件函数,生成记忆后的事件函数并传递给子组件使用。而 useMemo 更适合经过函数计算得到一个确定的值,比如记忆组件。
useCallback(fn, inputs) is equivalent to useMemo(() => fn, inputs).
useRef 返回的是一个可变的 ref 对象,它的属性 current 被初始化为传入的参数(initialValue),「返回的 ref对象在组件的整个生命周期内保持不变」。
作用:
例子:
import {useRef} from 'react';
export default () => {
const inputRef = useRef({value:0})
return (
<div>
<h1>测试</h1>
<input type="text" ref={inputRef} />
<button onClick={()=>{console.log(inputRef.current.value)}}>获取input 值</button>
<button onClick={()=>{inputRef.current.focus()}}>获取input 焦点</button>
</div>
)
}
在使用 Context 时,它通常用在顶级组件 (父组件上),它包裹的内部组件都可以享受到 state 的使用和修改。
通过 Context.Provider 来进行包裹,值通过 value = {} 传递。
子组件如何使用 Context 传递过来的值?
通过 useContext() Hook 可以很方便的拿到对应的值.
例子:
import React, { useState,useEffect,useContext } from 'react'
import axios from 'axios'
import './css/index.css'
const GlobalContext = React.createContext() //创建context对象
export default function App (){
const [filmList, setfilmList] = useState([])
const [info, setinfo] = useState("")
useEffect(() => {
axios.get(`/test.json`).then(res=>{
// console.log(res.data.data.films)
setfilmList(res.data.data.films)
})
}, [])
return (
<GlobalContext.Provider value={{
call:"打电话",
sms:"短信",
info:info,
changeInfo:(value)=>{
setinfo(value)
}
}}>
<div>
{/* {this.state.info} */}
{
filmList.map(item=>
<FilmItem key={item.filmId} {...item} ></FilmItem>
)
}
<FilmDetail ></FilmDetail>
</div>
</GlobalContext.Provider>
)
}
/*受控组件*/
function FilmItem(props){
let {name, poster,grade,synopsis} = props
const value = useContext(GlobalContext)
// console.log(context)
return <div className="filmitem" onClick={()=>{
console.log(synopsis)
value.changeInfo(synopsis)
}}>
<img src={poster} alt={name}/>
<h4>{name}</h4>
<div>观众评分:{grade}</div>
</div>
}
function FilmDetail(){
const value = useContext(GlobalContext)
return <div className="filmdetail">
detail-{value.info}
</div>
}
它是 useState 的替代方案。它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。
单组件例子:
import React,{useReducer} from 'react'
//处理函数
const reducer = (prevState,action)=>{
console.log("reduercer",prevState,action)
let newstate = {...prevState}
switch(action.type){
case "minus":
newstate.count--
return newstate
case "add":
newstate.count++
return newstate
default:
return prevState
}
}
// 外部的对象
const intialState = {
count:0,
// list:[]
}
export default function App() {
const [state, dispatch] = useReducer(reducer,intialState)
return (
<div>
<button onClick={()=>{
dispatch({
type:"minus"
})
}}>-</button>
{state.count}
<button onClick={()=>{
dispatch({
type:"add"
})
}}>+</button>
</div>
)
}
多组件例子:
import React,{useReducer,useContext} from 'react'
const initailState = {
a:"11111",
b:"11111"
}
const reducer = (prevState,action)=>{
let newstate = {...prevState}
switch(action.type){
case "change-a":
newstate.a = action.value
return newstate
case "change-b":
newstate.b = action.value
return newstate
default:
return prevState
}
// return prevState
}
const GlobalContext = React.createContext()
export default function App() {
const [state, dispatch] = useReducer(reducer, initailState)
return (
<GlobalContext.Provider value={
{
state,
dispatch
}
}>
<div>
<Child1/>
<Child2/>
<Child3/>
</div>
</GlobalContext.Provider>
)
}
function Child1(){
const {dispatch} = useContext(GlobalContext)
return <div style={{background:"red"}}>
<button onClick={()=>{
dispatch({
type:"change-a",
value:"2222222"
})
}}>改变a</button>
<button onClick={()=>{
dispatch({
type:"change-b",
value:"333333"
})
}}>改变b</button>
</div>
}
function Child2(){
const {state} = useContext(GlobalContext)
return <div style={{background:"yellow"}}>
child2-{state.a}
</div>
}
function Child3(){
const {state} = useContext(GlobalContext)
return <div style={{background:"gray"}}>
child3-{state.b}
</div>
}
当我们想在两个函数之间共享逻辑时,我们会把它提取到第三个函数中。
必须以“use”开头吗?必须如此。这个约定非常重要。不遵循的话,由于无法判断某个函数是否包含对其内部 Hook的调用,React 将无法自动检查你的 Hook 是否违反了 Hook 的规则
例子:
import React, { useState,useEffect,useMemo } from 'react'
import axios from 'axios'
function useCinemaList(){
const [cinemaList, setcinemaList] = useState([])
useEffect(() => {
axios({
url:"https://m.maizuo.com/gateway?cityId=110100&ticketFlag=1&k=7406159",
method:"get",
headers:{
'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.0.4","e":"16395416565231270166529","bc":"110100"}',
'X-Host': 'mall.film-ticket.cinema.list'
}
}).then(res=>{
setcinemaList(res.data.data.cinemas)
})
}, [])
return {
cinemaList
}
}
function useFilter(cinemaList,mytext){
const getCinemaList = useMemo(() => cinemaList.filter(item=>item.name.toUpperCase().includes(mytext.toUpperCase()) ||
item.address.toUpperCase().includes(mytext.toUpperCase())
), [cinemaList,mytext])
return {
getCinemaList
}
}
export default function Cinema(){
const [mytext, setmytext] = useState("")
const {cinemaList} = useCinemaList()
const {getCinemaList} = useFilter(cinemaList,mytext)
return <div>
{/* {this.state.mytext} */}
<input value={mytext} onChange={(evt)=>{
setmytext(evt.target.value)
}}/>
{
getCinemaList.map(item=>
<dl key={item.cinemaId}>
<dt>{item.name}</dt>
<dd>{item.address}</dd>
</dl>
)
}
</div>
}
路由是根据不同的 url 地址展示不同的内容或页面。
一个针对React而设计的路由解决方案、可以友好的帮你解决React components 到URl之间的同步映射关系。
react 路由 安装文档
注:最新的版本为6,后续会补充。
//安装语句
npm install react-router-dom@5
import React from "react";
import {BrowserRouter as Router, Switch,Route, Link} from "react-router-dom";
<HashRouter>
<Switch>
<Route path="/films" component={ Films}/>
<Route path="/cinemas" component={ Cinemas}/>
<Route path="/center" component={ Center}/>
<Redirect from="/" to="/films" />
{/*
*/ }
</Switch>
</HashRouter>
注意:
<Switch>
<Route path="/films/nowplaying" component={ Nowplaying}/>
<Route path="/films/comingsoon" component={ Comingsoon}/>
<Redirect from="/films" to= "/films/nowplaying"/>
</Switch>
<NavLink to="/films" activeClassName= "active">films</NavLink>
<NavLink to="/cinemas" activeClassName= "active">cinemas</NavLink>
<NavLink to="/center" activeClassName= "active">center</NavLink>
this.props.history.push(`/center`)
this.props.history.push({ pathname : '/user' ,query : { day: 'Friday'} }) this.props.location.query.day
this.props.history.push({ pathname: '/user',state:{day : 'Friday' } })
this. props.location.state.day
<Route path="/center" render={() =>isAuth()?<Center/>:<Login/>} />
import { withRouter } from "react-router";
withRouter(MyComponent);
withRouter(connect( ...)(MyComponent))
链接: 文档
npm install http-proxy-middleware --save
const { createProxyMiddleware } = require( 'http-proxy-middleware');
module.exports = function(app) { app. use(
'/api', createProxyMiddleware({
target: 'http://localhost:5000', changeOrigin: true,
})
);
};
链接: 文档
全局
:global(.active){
XXXX
}