1.介绍
官网https://react.docschina.org/tutorial/tutorial.html
1.1概述
是一个用于构建用户界面的JavaScript库。
1.2初体验
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>react初体验</title> <script src="https://unpkg.com/react@16/umd/react.development.js"></script> <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> </head> <body> <div id="app"></div> <script type="text/babel"> const h = <h1>hello world</h1> ReactDOM.render(h, document.getElementById("app")) </script> </body> </html>
运行这个html文件即可在页面看到hello world。上例中引入了3个js文件,其中react.js是核心库,react-dom.js提供了DOM的react扩展库。babel.js是把JSX语法转为纯js语法的库。需要注意的是,在script标签中使用react时,type必须是“text/babel”。
2.JSX
2.1JSX的定义
在初体验中,有这样一行代码:
const h = <h1>hello world</h1>
这个标签语法既不是字符串也不是 HTML,而是被称为 JSX(全称JavaScript XML),是一个 JavaScript 的语法扩展。JSX 语法上更接近 JavaScript 而不是 HTML,所以 React DOM 使用 camelCase
(小驼峰命名)来定义属性的名称,而不使用 HTML 属性名称的命名约定。当jsx有多行代码时,可以使用括号把这些jsx标签包裹起来,看起来比较规范。
2.2在JSX中嵌入表达式
const name = '张三' const h = <h1>欢迎你,{ name }</h1>
首先声明一个名为 name
的变量,然后使用大括号包起来,就可以使用。不仅如此,还可以在大括号中放任何有效的js表达式,jsx实际上也是一个表达式。
实例:在jsx中使用变量和方法
function sum(a1, a2) { return a1 + a2 } const a = 10, b = 20 const h = <h1>求和:{a}+{b}={sum(a, b)}</h1> ReactDOM.render(h, document.getElementById("app"))
2.3JSX特定属性
在JSX中,给标签属性添加值,使用引号或大扩号,两者只能使用一个。假如一个标签里面没有内容,你可以使用 /> 来闭合标签。另外React DOM 在渲染所有输入内容之前,默认会进行转义,以确保在应用中永远不会注入那些并非自己明确编写的内容。所有的内容在渲染之前都被转换成了字符串,可以有效地防止 XSS攻击。
1)使用引号。和原来类似,需要注意的是指定类名换成了className,而不是class。
const h = <h1 className="myH1">hello world</h1> ReactDOM.render(h, document.getElementById("app"))
2)使用大括号插入表达式
const url='4.png' const img=<img src={url}/> ReactDOM.render(img, document.getElementById("app"))
实例:动态的显示一个数组为li
const arr = ['java', 'c++', 'c#', 'python'] const ul = <ul>{arr.map((name, index) => <li key={index}>{name}</li>)}</ul> ReactDOM.render(ul, document.getElementById("app"))
这里用到了数组的map方法,它会遍历数组的每一个内容,并返回操作后的内容。在使用li标签时,指定了key,即给每一个元素指定了一个唯一的标识,也就是在其兄弟节点之间应该是独一无二的。然而,它们不需要是全局唯一的。当生成两个不同的数组时,可以使用相同的 key 值,这是react要求的。效果图如下:
2.4JSX表示对象
Babel 会把 JSX 转译成一个名为 React.createElement()
函数调用,然后把JSX语法转为js语法。
以下两种示例代码完全等效:
1)使用jsx语法
const h = <h1 className="myH1">hello world</h1>
2)不使用jsx语法
const h = React.createElement( 'h1', {className:'myH1'}, 'hello world' )
这种语法,通过createElement函数分别指定标签名、属性对象以及内容。相比起来,这种还是麻烦的,不常用。
3.组件
3.1定义组件
3.1.1函数组件
定义组件最简单的方式就是编写 JavaScript 函数,然后在标签中使用即可,这种方式比较简单。
<script type="text/babel"> function Welcome() { return <h1>Hello, 组件</h1>; } const element = <Welcome/> ReactDOM.render(element, document.getElementById("app")) </script>
3.1.2class组件
当然还可以使用ES6的class来定义组件。这种方式比较复杂,非常适用于有状态的组件。
<script type="text/babel"> class Welcome extends React.Component{ render(){ return <h1>你好啊,组件</h1> } } const element = <Welcome/> ReactDOM.render(element, document.getElementById("app")) </script>
函数组件和class组件还是有区别的,最大的区别就是,在函数组件中props和方法的调用不需要this,而class组件中都需要。
3.2组件属性state
state叫做状态,是组件对象最重要的属性,其值是对象类型。当修改了其值,那么组件就会执行render()方法来重新渲染。如果其他组件也需要这个 state,那么可以将它提升至这些组件的最近共同父组件中。
3.2.1操作语法
1)初始化状态
第一种方法
class App extends React.Component{ constructor(props){ super(props) //初始化状态 this.state={ stateName:value } } }
第二种方法
class App extends React.Component{ //初始化状态,不在构造函数中设置 state={ stateName:value } }
2)获取状态
this.state.stateName
3)修改状态。规则是状态在哪个组件,就在此组件更新状态。
this.setState({ stateName:value })
3.2.2基本使用
实例:通过点击按钮,来显示不同的两种值
<script type="text/babel"> class App extends React.Component { constructor(props) { super(props) //初始化状态 this.state = { count: false } //把新添加的方法中this强制绑定为组件对象 this.clickEvent = this.clickEvent.bind(this) } render() { //解构方式读取状态 const { count } = this.state return ( <div> <h2>{count ? '你好啊' : '我很好'}</h2> <button onClick={this.clickEvent}>切换</button> </div> ) } //新添加的方法,内部的this默认不是组件对象 clickEvent() { this.setState({ count: !this.state.count }) } } //简写的方式,直接使用组件 ReactDOM.render(<App/>, document.getElementById("app")) </script>
通过点击按钮,可以来回切换内容的显示。
需要注意的是:1)新添加的方法,内部的this默认不是组件对象而是undefined,如果要使用this,必须把这个this强制转换为组件对象。另外这个实例中出现的onClick事件,在后面会介绍。
2)不要直接修改state的值,this.state.count=3这种方式是不正确的,也是不生效的。必须通过setState进行更新。
在上例中,设置状态是在构造函数中进行的,也有另一种方法,在类中设置即可。
class App extends React.Component { constructor(props) { super(props) //把新添加的方法中this强制绑定为组件对象 this.clickEvent = this.clickEvent.bind(this) } //初始化状态 state = { count: false } ... }
另外,在构造函数中绑定了clickEvent方法的this,其实也可以不绑定,只要把此方法通过箭头函数定义即可。这样一来,可以不使用构造函数,减少代码量。
clickEvent = () => { this.setState({ count: !this.state.count }) }
3.3组件属性props
当组件进行通信时,可以使用props属性,以函数组件为例。当子组件想要使用父组件的方法时,也是可以通过props的方法,把方法名传递给子组件,然后子组件通过props来调用相应的方法即可。
1)直接传递参数(变量)
<script type="text/babel"> function Welcome(props) { return <h1>Hello, 组件{props.name}</h1>; } const element = <Welcome name="张三"/> ReactDOM.render(element, document.getElementById("app")) </script>
在这个例子中,定义组件时,接收了props的参数,所有的值都在里面,使用了其中的name属性。使用组件时,传入了name的值,这样就可以在组件内部使用这个name的值。
2)给props设置默认值
有时候,有些参数不是必须传递的,可以通过设置默认值进行显示。
const person = { name: '张三', age: 18, sex: '男' } function Welcome(props) { return ( <ul> <li>姓名:{props.name}</li> <li>性别:{props.sex}</li> <li>年龄:{props.age}</li> </ul> ) } //设置props默认值 Welcome.defaultProps = { sex:'女', age:20 } const element = <Welcome name={person.name} /> ReactDOM.render(element, document.getElementById("app"))
3)给props指定参数类型和必传类型
在使用前,需要导入一个js或通过npm安装
直接引入js
<script src="https://cdn.bootcdn.net/ajax/libs/prop-types/15.5.10/prop-types.js"></script>
npm安装
npm i prop-types -S
有些参数是必须要传递的,需要设置必须传递。也可以对传递的参数进行类型限制。
const person = { name: '张三', age: '18', sex: '男' } function Welcome(props) { return ( <ul> <li>姓名:{props.name}</li> <li>性别:{props.sex}</li> <li>年龄:{props.age}</li> </ul> ) } //指定属性值的类型和必传类型 Welcome.propTypes={ //同时指定name是string类型和必传 name:PropTypes.string.isRequired, age:PropTypes.number } const element = <Welcome name={person.name} sex={person.sex} age={person.age} /> ReactDOM.render(element, document.getElementById("app"))
对于上面的代码,age指定的是number类型,传递的是string类型,浏览器就会报错。指定了name是必传的,如果不传也会报错。当需要传递的是一个对象的所有属性时,可以使用ES6的扩展运算符简写:
const element = <Welcome {...person} />
另外,如果是用class组件,那么就没有props参数,但是这个值存在this中,使用this.props同样可以获取。
在class组件中,可以把props的类型判断放到类内部通过static修饰,不使用上面的那种方式,3的class组件如下:
const person = { name: '张三', age: 18, sex: '男' } class Welcome extends React.Component { //指定属性值的类型和必传类型 static propTypes = { //同时指定name是string类型和必传 name: PropTypes.string.isRequired, age: PropTypes.number } render() { const props = this.props return ( <ul> <li>姓名:{props.name}</li> <li>性别:{props.sex}</li> <li>年龄:{props.age}</li> </ul> ) } } const element = <Welcome name={person.name} sex={person.sex} age={person.age} /> ReactDOM.render(element, document.getElementById("app"))
当然,也可以参照此例把设置默认值放到class内部
static defaultProps = {
sex: '女',
age: 20
}
3.4组件属性ref
通过ref,可以找到对应的组件,即用来标识组件的某个元素。
实例1:点击按钮,获取文本框的值
class App extends React.Component { constructor(props) { super(props) //把新添加的方法中this强制绑定为组件对象 this.clickEvent = this.clickEvent.bind(this) } render() { return ( <div> <input type="text" ref={input =>this.inputValue=input}/> <button onClick={this.clickEvent}>获取值</button> </div> ) } //新添加的方法,内部的this默认不是组件对象 clickEvent() { alert(this.inputValue.value) } } ReactDOM.render(<App/>, document.getElementById("app"))
本例中,在input标签中添加了ref属性,里面是一个input方法。
实例2:对组件的三个属性进行综合使用,做一个简单的数据添加,然后动态的显示
//组件1,父组件
class App extends React.Component { constructor(props) { super(props) //状态中存入数据 this.state = { list: ['C++', 'python', 'C语言', 'java'] } this.addList = this.addList.bind(this) } //父组件添加数据,修改状态 addList(value) { let { list } = this.state list.unshift(value) this.setState({ list }) } render() { const { list } = this.state return ( <div> <h1>简单的数据动态显示</h1> <Add count={list.length} addList={this.addList} /> <List list={list} /> </div> ) } }
//组件2,添加数据的组件 class Add extends React.Component { constructor(props) { super(props) this.addHandle = this.addHandle.bind(this) } addHandle() { const val = this.inputValue.value.trim() if (val) { //调用父组件传递过来的方法 this.props.addList(val) } //清空文本框 this.inputValue.value = '' } render() { return ( <div> <input type="text" ref={input => this.inputValue = input} /> <button onClick={this.addHandle}>添加 #{this.props.count + 1}</button> </div> ) } }
//组件3,数据展示的组件 class List extends React.Component { constructor(props) { super(props) } render() {
//在class组件中,使用this.props来获取传递的参数 const { list } = this.props return ( <div> <ul> {list.map((item, index) => <li key={index}>{item}</li>)} </ul> </div> ) } } //对传入的类型进行判断 Add.propType = { count: PropTypes.number.isRequire,
//接收参数是函数,必传 addList: PropTypes.func.isRequire } List.propType = { list: PropTypes.array.isRequire }
//渲染组件 ReactDOM.render(<App />, document.getElementById("app"))
当点击添加按钮时,就会把数据添加到数组中,然后显示在页面上。截图如下:
在这个实例中,需要注意的是,子组件是不能改变父组件的状态的。使用的数据存放在状态state中,组件之间的传值使用props,获取组件的某个元素使用ref。
3.5组件的生命周期
1)挂载(mount):componentDidMount()
组件第一次渲染完成时期,此时dom节点已经生成。
2)卸载(unmount):componentWillUnmount ()
在此处完成组件的卸载和数据的销毁。
实例:动态的显示系统时间,点击关闭时就清除组件内容
class App extends React.Component { constructor(props) { super(props) //状态中存入数据 this.state = { date: new Date() } } //组件挂载时设置定时器 componentDidMount() { this.timerID = setInterval(() => { this.setState({ date:new Date() }) },1000); } //在卸载组件之前删除组件 componentWillUnmount(){ //清除定时器 clearInterval(this.timerID) } //删除组件 destoryComponent(){ //通过dom的id删除对应元素 ReactDOM.unmountComponentAtNode(document.getElementById('app')) } render() { const { list } = this.state return ( <div> <h2>现在时间:{this.state.date.toLocaleTimeString()}</h2> <button onClick={this.destoryComponent}>删除时间组件</button> </div> ) } } ReactDOM.render(<App />, document.getElementById('app'))
本例通过ReactDOM实例的unmountComponentAtNode()方法来删除组件。
3.6组件嵌套
3.6.1使用props.children
//侧边栏,通过children将他们的子组件到渲染结果中 function Sidebar(props) { return ( <div> {props.children} </div> ) } //在组件中使用其他的组件,组件Sidebar内的内容将会作为children通过props传递给Sidebar function Welcome(props) { return ( <div> <h1>菜单栏</h1> <Sidebar> <p>用户管理</p> <p>菜单管理</p> <p>系统管理</p> </Sidebar> </div> ) } ReactDOM.render(<Welcome />, document.getElementById("app"))
3.6.2自行约定
将所需内容传入 props,并使用相应的 prop。
function Sidebar(props) { return ( <div> <div className="left"> <h2>菜单区</h2> {props.left} </div> <div className="right"> <h2>内容区</h2> {props.right} </div> </div> ) } //通过props传递jsx function Welcome() { return ( <div> <Sidebar left={<p>我是左边菜单栏</p>} right={<p>我是右边显示具体内容</p>}/> </div> ) } ReactDOM.render(<Welcome />, document.getElementById("app"))
3.7组件通信Context
除了使用props进行组件之间的传值之外,还可以使用context进行组件之间的通信。当多个嵌套的组件之间进行传值用到同一个变量时,使用props就显得非常的冗杂。还有一种情况就是当两个兄弟组件需要使用同一数据时,目前的办法就是定义一个公共的父组件,把数据放到父组件中,也是比较麻烦的。context提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。
3.7.1深层嵌套组件使用props案例
class ItemIndex extends React.Component { render() { return ( <span>{this.props.index}-----</span> ) } } class DataItem extends React.Component { render() { return ( <div><ItemIndex index={this.props.index} />{this.props.item}</div> ) } } class DataList extends React.Component { render() { const { list } = this.props return ( <ul> {list.map((item, index) => <DataItem index={index} key={index} item={item} />)} </ul> ) } } class Welcome extends React.Component { render() { const list = ['C++', 'python', 'java'] return ( <DataList list={list} /> ) } } ReactDOM.render(<Welcome />, document.getElementById("app"))
这个实例中组件DataItem接收了index,但是它没有使用,又传递给了ItemIndex组件,这就是层层传递的问题。
3.7.2使用context解决
全局定义一个context对象,然后在需要传递值的组件外部添加一个provider将值深入传递进组件树,直到在需要的地方使用。在使用时,设置context类型为全局定义的context,然后使用this.context获取。上面实例的代码替换如下:
//创建context对象,light是默认值 const ThemeContext = React.createContext('light') class ItemIndex extends React.Component { //设置context类型,使用this.context获取 static contextType = ThemeContext render() { return ( <span>{this.context}-----</span> ) } } class DataItem extends React.Component { render() { return ( <div><ItemIndex />{this.props.item}</div> ) } } class DataList extends React.Component { render() { const { list } = this.props return ( <ul> {list.map((item, index) => <ThemeContext.Provider value={index}> <DataItem key={index} item={item} /> </ThemeContext.Provider>) } </ul> ) } } class Welcome extends React.Component { render() { const list = ['C++', 'python', 'java'] return ( <DataList list={list} /> ) } } ReactDOM.render(<Welcome />, document.getElementById("app"))
效果和上面实例的效果一样。实例中context对象放在同一个js文件中,当需要在不同的js文件中使用是,就把创建context对象的代码放到一个js文件,其他文件需要时引入即可。
3.8组件通信消息订阅发布机制
也可以使用消息订阅(subscribe)-发布(publish)机制来解决兄弟组件传递数据的问题。
3.8.1安装
安装
npm i pubsub-js -S
导入
import pubsub from 'pubsub-js'
3.8.2使用
1)订阅
Pubsub.subscribe('messageName',function(msg,data){ })
订阅后指定消息的名字以及回调函数,包含回调信息和数据。
2)发布
Pubsub.publish('messageName',data)
指定消息的名字和要传递的数据。
3)组件中使用
class DataItem extends React.Component { state={ msg : '我是来自DataItem的信息' } //挂载后发布信息 componentDidMount() { Pubsub.publish('message', this.state.msg) } render() { return ( <div>{this.state.msg}</div> ) } } class DataItem2 extends React.Component { state={ message:'' } //挂载后订阅信息 componentDidMount() { Pubsub.subscribe('message', (msg, data) => { this.setState({message: data}) }) } render() { return ( <div>我收到来自兄弟的信息:{this.state.message}</div> ) } } class Welcome extends React.Component { render() { return ( <div><DataItem /> <DataItem2 /></div> ) } } ReactDOM.render(<Welcome />, document.getElementById("app"))
4.事件处理
React 事件的命名采用小驼峰式(camelCase),而不是纯小写。在使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。
4.1点击事件onClick
1)直接的点击触发
function handleEvent(){ alert("我是react事件") } function MyButton(){ return <button onClick={handleEvent}>点我触发点击事件</button> } ReactDOM.render(<MyButton />,document.getElementById('app'))
本例没有传递参数,实际上在事件方法中有一个默认的参数event,在需要用的时候加入即可。
function handleEvent(e) { alert("我是react事件") console.log(e) }
2)阻止默认行为
//函数组件
function ActionLink() {
//点击事件要执行的方法 function handleClick(e) { e.preventDefault();//阻止标签的默认行为 console.log('The link was clicked.'); } return ( <a href="#" onClick={handleClick}> Click me </a> ) } ReactDOM.render(<ActionLink />,document.getElementById('app'))
对比1和2可以看出,一个事件的触发方法是放在函数组件的外面,一个放在函数组件的里面,这都是可以的,但是在class组件中只能放在类内部。
3)给点击事件传递参数
点击的事件,有时也需要传递参数,有两种方式。
第一种:使用箭头函数在方法中传递
function handleEvent(p,e) { alert("我是react事件,传递参数是:" + p)
console.log(e) } function MyButton() { return <button onClick={e => handleEvent(123, e)}>点我触发点击事件</button> } ReactDOM.render(<MyButton />, document.getElementById('app'))
第二种:通过bind绑定机制传递
function handleEvent(p, e) { alert("我是react事件,传递参数是:" + p)
console.log(e) } function MyButton() { return <button onClick={handleEvent.bind(this,123)}>点我触发点击事件</button> }
这两种方式效果都是一样的,只不过对于事件event,一个是显式传递,一个是隐式传递。传递后要获取事件对象event,都是通过最后一个参数来的,前面的参数是传递的形参。
4.2内容改变事件onChange
和原生的onchange事件同。
5.表单
5.1受控组件和非受控组件
1)受控组件:表单输入的数据能够自动存到state状态中
2)非受控组件:需要时才手动读取表单的数据
一般情况下,都会把表单输入的数据作为受控组件使用。
5.2input标签
1)输入框text
class App extends React.Component { constructor(props) { super(props) this.state = { value: '' } //绑定this this.changeHandle = this.changeHandle.bind(this) this.submitHandle = this.submitHandle.bind(this) } changeHandle(event) { this.setState({ value: event.target.value }) } submitHandle(event) { alert(this.state.value) event.preventDefault() } render() { return ( <form onSubmit={this.submitHandle}> 用户名:<input type='text' onChange={this.changeHandle} /> <button type="submit">提交</button> </form> ) } }
点击按钮时打印文本框输入的值。通过onChange事件,来不断的更新state里面的值。
2)单选框radio
render() { const {value}=this.state return ( <form onSubmit={this.submitHandle}> 性别: <input type='radio' name="sex" onChange={this.changeHandle} value="0" checked={value==0}/>男 <input type='radio' name="sex" onChange={this.changeHandle} value="1" checked={value==1}/>女 <button type="submit">提交</button> </form> ) }
3)复选框checkbox
4)多文本框输入
当需要处理多个文本框时,可以根据元素的name属性来判断event.target.name要执行的操作。
class App extends React.Component { constructor(props) { super(props) this.state = { username:'', password:'' } //绑定this this.changeHandle = this.changeHandle.bind(this) this.submitHandle = this.submitHandle.bind(this) } changeHandle(event) { const target = event.target //根据不同的name获取不同的值 const name = target.name this.setState({ // 使用ES6 计算属性名称的语法更新给定输入名称对应的 state 值 [name]: target.value }) } submitHandle(event) { alert(this.state.username+","+this.state.password) event.preventDefault() } render() { const {value}=this.state return ( <form onSubmit={this.submitHandle}> 用户名:<input type='text' name="username" onChange={this.changeHandle} /> 密码:<input type='password' name="password" onChange={this.changeHandle} /> <button type="submit">提交</button> </form> ) } }
5.3textarea标签
class App extends React.Component { constructor(props) { super(props) this.state = { value: '请输入信息' } //绑定this this.changeHandle = this.changeHandle.bind(this) this.submitHandle = this.submitHandle.bind(this) } changeHandle(event) { this.setState({ value: event.target.value }) } submitHandle(event) { alert(this.state.value) event.preventDefault() } render() { return ( <form onSubmit={this.submitHandle}> 输入信息:<textarea onChange={this.changeHandle} value={this.state.value} /> <button onClick={this.submitHandle}>提交</button> </form> ) } }
5.4select标签
React的select在根 select
标签上使用 value
属性来选中某个选项。
class App extends React.Component { constructor(props) { super(props) this.state = { value: '1' } //绑定this this.changeHandle = this.changeHandle.bind(this) this.submitHandle = this.submitHandle.bind(this) } changeHandle(event) { this.setState({ value: event.target.value }) } submitHandle(event) { alert(this.state.value) event.preventDefault() } render() { return ( <form onSubmit={this.submitHandle}> 选择性别:<select onChange={this.changeHandle} value={this.state.value}> <option value="0">男</option> <option value="1">女</option> </select> <button onClick={this.submitHandle}>提交</button> </form> ) } }
选择性别时,默认state是1,就选中了女,通过这种方式设置默认选中的值。
6.条件渲染
根据不同的值渲染不同的组件。
6.1元素变量
实例:根据state中变量的值,来渲染对应的组件
class Welcome extends React.Component { state = { isLogin: false } render() { const { isLogin } = this.state //根据条件的不同,渲染不同的组件 if (isLogin) { return ( <Login /> ) } else { return ( <Regist /> ) } } } class Login extends React.Component { render() { return (<div>登录页面</div>) } } class Regist extends React.Component { render() { return (<div>注册页面</div>) } } ReactDOM.render(<Welcome />, document.getElementById("app"))
6.2与运算符&&
前面介绍jsx的{ }时已经说明,可以在里面放任何表达式,因此可以放入&&进行运算。
render() { return ( <div> <h1>欢迎来到react世界</h1> {1 < 0 && <h2>哈哈哈</h2>} </div> ) }
代码中由于1<0不成立,因此不会显示h2标签的内容。
6.3三目运算符
根据值的不同显示不同的表达式。
render() { const flag = true return ( <div> <h1>欢迎来到react世界</h1> <p>用户{flag?'已':'未'}登录</p> </div> ) }
6.4阻止组件的渲染
在极少数情况下,希望能隐藏组件,即使它已经被其他组件渲染。若要完成此操作,可以让 render
方法直接返回 null
,而不进行任何渲染。
function WarningBanner(props) { if (!props.warn) { return null; } return ( <div className="warning"> Warning! </div> ); }
此组件会根据 prop 中 warn
的值来进行条件渲染。如果 warn
的值是 false
,那么组件则不会渲染。
7.整合axios
整合axios非常简单,引入js,然后发送对应的请求即可。
1)引入js
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.1.0/axios.min.js"></script>
使用npm安装
npm i axios -S
2)获取笑话的完整代码
<script type="text/babel"> class App extends React.Component { //状态中存入数据 state = { num: 10, jokes: [] } //组件挂载 componentDidMount() { this.getJokes() } getJokes = () => { let { num, jokes } = this.state console.log(num) //发送异步请求 const url = 'https://autumnfish.cn/api/joke/list' axios.get(url, { params: { num } }).then(res => { jokes = [...jokes, ...res.jokes] this.setState({ jokes }) }, err => { console.log(err) }) } render() { const { num, jokes } = this.state if (jokes.length <= 0) { return <h2>加载中,请稍后...</h2> } else { return ( <div> <button onClick={this.getJokes}>获取笑话+10</button> {jokes.map((item, index) => <li key={index}>{index+1+"."+item}</li>)} </div> ) } } } ReactDOM.render(<App />, document.getElementById('app')) </script>
点击一次按钮,就请求10条数据,追加显示在页面上。
8.Fragments
React 中的一个常见模式是一个组件返回多个元素。Fragments 允许你将子列表分组,而无需向 DOM 添加额外节点。
8.1不使用Fragments的案例
class Table extends React.Component { render() { return ( <table> <tr> <Columns /> </tr> </table> ); } } class Columns extends React.Component { render() { return ( <div> <td>Hello</td> <td>World</td> </div> ); } } ReactDOM.render(<Table />, document.getElementById("app"))
这样页面渲染的table格式如下,很明显在tr到td的标签时中间加入了div标签,原因是在Columns组件中定义了父组件时包裹了div。如果不需要插入这个div标签,就可以使用Fragments
8.2使用Fragments案例
class Table extends React.Component { render() { return ( <table> <tr> <Columns /> </tr> </table> ); } } class Columns extends React.Component { render() { return ( <React.Fragment> <td>Hello</td> <td>World</td> </React.Fragment> ); } } ReactDOM.render(<Table />, document.getElementById("app"))
此案例中,使用<React.Fragment>替换了div标签,它就不会额外的插入不需要的标签。页面渲染的table格式如下
8.3Fragment扩展
<React.Fragment>还有一种短语法,直接写两个括号即可,如下:
class Columns extends React.Component { render() { return ( <> <td>Hello</td> <td>World</td> </> ); } }
如果将一个集合映射到一个 Fragments 数组,那么Fragment需要带key,就把key赋值给它即可
function ItemList(props) { return ( <dl> {props.items.map(item => ( <React.Fragment key={item.id}> <dt>{item.term}</dt> <dd>{item.description}</dd> </React.Fragment> ))} </dl> ); }
..