React基础

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通过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)阻止默认行为

在 React 中不能通过返回 false 的方式阻止默认行为,必须显式的preventDefault来阻止。
       //函数组件 
    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标签

在React 中,<textarea> 使用 value 属性代替子元素的文本信息。
     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>
  );
}

..

就是这么简单,你学废了吗?感觉有用的话,给笔者点个赞吧 !
原文地址:https://www.cnblogs.com/zys2019/p/13596195.html