React三剑客: state props refs

90 阅读2分钟

jsx语法规则:

  1. 定义虚拟dom, 不要使用引号
  2. 标签中混入JS表达式要用{}
  3. 样式的类名不要使用class, 要使用className
  4. 内联样式, 要用style = {{key: value}}的形式
  5. 只有一个根标签
  6. 标签必须闭合
  7. 标签的字母
      1. 若小写字母开头, 规则将标签转为html中同名元素, 若没有则报错
      2. 若大写字母开头, react就会去渲染对应的组件, 若组件没有则报错

模块:

  理解: 向外部提供特定功能的js程序, 一般就是一个js文件
  作用: 复用js, 简化js的编写

模块化:

  当应用的js都以模块的方式来编写, 就可以成为模块化应用

组件:

  理解: 用来实现局部功能效果的代码和资源的集合(html/js/image等等)
  作用: 复用代码, 简化项目编码

组件化:

  当项目多以组件的方式来实现, 就可以成为组件化
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>JS Bin</title>
  </head>
  <body>
  
  <!-- react 核心库  必须先导入这个-->
  <script src="https://fb.me/react-15.1.0.js"></script>
  <!-- react 操作dom的库  -->
  <script src="https://fb.me/react-dom-15.1.0.js"></script>
  <!-- babel  将jsx转为js的库 -->
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> 

    <div id="app"> </div>
    
    <script type="text/babel">
    // 1. 准备数据
       const data = ['Angular', 'React', 'Vue']
       
       // 2. 创建虚拟dom
       const vDom = (
         <div> 
             <h1>前端技术框架</h1>
             <ul>
               {
                  // 这里只允许放js表达式(有返回值), 不允许放js语句
                
                 data.map((str, index) => {
                   return <li key={index}> { str } </li>
                 })
               }
             </ul>
         </div>
       )
       
       // 3. 渲染
       ReactDOM.render(vDom, document.getElementById('app'))
    </script>
  
  </body>
  </html>

函数式组件

<script type="text/babel">
        // 1. 创建函数式组件
        // 组件首字母必须大写,  参照jsx规则
        function Dom() {
            console.log(this) // 这里的this 是undefine 因为babel 转换成js之后, 开启了严格模式, 导致自定义函数this 不会指向window
            // babel 官网试一试, 可以查看babel翻译后的js
            return <h1> hello react</h1>
        }

        // 这里渲染的时候需要注意,  不能直接写 Dom , 必须< /> 必须要闭合 参照jsx规则
        ReactDOM.render(<Dom />, document.getElementById('app'))
        /*
            1. React解析组件标签, 找到Dom
            2. 发现组件是使用函数㐉的, 随后调用函数, 将返回的虚拟dom转为真实的dom
        */
    </script>

class

 <script>
        class Person {
            // 构造器函数, 不是必须要写的, 赋值才有必要
            constructor(name, age) {
                // 这里的this 是类的实例对象
                this.name = name
                this.age = age
            }
            
            a = 1 // 直接被挂到实例对象上
            
            // 这个函数也会被放到实例对象上
            // 函数如果被直接调用, speak里的this为null,  
            // 赋值函数里的this 会自动向外层找, 找到类实例对象
            eat = () => {
                
            }

            // 普通的函数  方法被放到了原型对象上
            speak() {
                // 这里的this 看是谁调用的, 
                // [注意]  调用函数 不仅仅有 实例.函数 还有 实例.call/apply/bind 都能更改this的指向
                console.log(`my name is ${this.name}, age is ${this.age}`)
            }
        }

        const p1 = new Person('jak', 20)
        console.log(p1)

    </script>

image.png

class组件

1. class 组件 必须继承 React.Component 
2. 构造函数必须实现render() {}  render里 必须有一个返回 
3. 构造函数不是必须实现的, 只有两种情况需要实现构造函数
    . 在构造函数里为state 赋值
    . 在构造函数里绑定函数
   
<script type="text/babel">
        // 1. 创建class组件
        class Dom extends React.Component {
            render() {
                return (
                    <h1>hello class component</h1>
                )
            }
        }

        ReactDOM.render(<Dom />, document.getElementById('app'))
    </script>

state(数据驱动UI), props(传递参数), refs(绑定事件) 三剑客

image.png

state的使用

<script type="text/babel">
        // 1. 创建class组件
        class Dom extends React.Component {
            // 如果使用构造函数, 必须调用super
            constructor(props) {
                super(props)
                this.state = {isHot: false}
            }

            render() {
                // 解构赋值
                const { isHot} = this.state

                return (
                    <h1 onClick={this.changeWeather}>hello class component {isHot ? "hot" : 'cool'}</h1>
                )
            }
            
            changeWeather() {
                // 这个时候 的this是空的
                // changeWeather 是被直接调用的, 被调用的时候内部使用了 严格模式, 导致this指向为空
                console.log(this)
            }
        }

        ReactDOM.render(<Dom />, document.getElementById('app'))
    </script>

上面的示例里, changeWeather中this 为空的, 为空就拿不到state, 拿不到state就无法修改值, 所以要改动一下

    // 构造器里拿到的this 就是实例本身, 使用bind函数进行绑定就可以了 
    constructor(props) {
        super(props)
        this.state = {isHot: false}
        this.changeWeather = this.changeWeather.bind(this)
    }

如果想要修改state里的值, 那么必须调用setState方法, 不然无法改变

    changeWeather() {
        const {isHot} = this.state
        this.setState({isHot: !isHot}) // 必须调用setState修改才可以驱动UI的更改
    }

也可以修改成下面这种样式

<script type="text/babel">
        // 1. 创建class组件
        class Dom extends React.Component {
            // constructor(props) {
            //     super(props)
            //     this.state = {isHot: false}
            //     this.changeWeather = this.changeWeather.bind(this)
            // }
            
            // 根据类的特性,  直接赋值会被挂到实例对象上, 初始化可以这么写
            state = {isHot: false}


            render() {
                const { isHot} = this.state

                return (
                    <h1 onClick={this.changeWeather}>hello class component {isHot ? "hot" : 'cool'}</h1>
                )
            }

            // 根据类的特性, 赋值函数可以被挂到实例对象上, 并且箭头函数里的this会自动向上寻找
            changeWeather = () => {
                const {isHot} = this.state
                this.setState({isHot: !isHot})
            }
        }

        ReactDOM.render(<Dom />, document.getElementById('app'))
    </script>

使用props进行传值

<script type="text/babel">
        // 1. 创建class组件
        class Dom extends React.Component {

            render() {
                const { name, sex, age } = this.props
                return (
                    <ul>
                        <li>姓名: { name }</li>
                        <li>性别: { sex }</li>
                        <li>年龄: { age }</li>
                    </ul>
                )
            }
        }
        
        // 可以使用这种展开的形式进行传递  
        // 注意: 原生是不允许展开对象的, 但是这里因为有babel 的转换{...p}
        const p = {name:'kk', age:10, sex:'男'}
        ReactDOM.render(<Dom {...p} />, document.getElementById('app'))
        
        // 可以直接用参数单个传递
        ReactDOM.render(<Dom name='cc' sex='女' age='18'/>, document.getElementById('app1'))
    </script>

可以对传递参数的类型进行限制

<script type="text/babel">
        // 1. 创建class组件
        class Dom extends React.Component {

            constructor(props) {
                super(props)
                // 使用这种方式, 这里调用的方式 类.proTypes 所以下面的 protypes 和 defaultProps 需要加上static
                // 表示是在类上
                PropTypes.checkPropTypes(Dom.propTypes, props, 'prop', 'Dom');
            }

            render() {
                const { name, sex, age } = this.props
                return (
                    <ul>
                        <li>姓名: { name }</li>
                        <li>性别: { sex }</li>
                        <li>年龄: { age + 1 }</li>
                    </ul>
                )
            }

            static propTypes = {
                name: PropTypes.string.isRequired,
                age: PropTypes.number,
                sex: PropTypes.string,
                speak: PropTypes.func // 注意, 这里的函数为func 
            }

            static defaultProps = {
                sex: '男',
                age: 18
            }
        }

        ReactDOM.render(<Dom  name='kk' age={18} speak={ speak}/>, document.getElementById('app1'))

        function speak() {
            console.log('1111')
        }
    </script>

refs 事件绑定

字符串形式的ref

 <script type="text/babel">
        // 1. 创建class组件
        class Dom extends React.Component {


            showAlert = () => {
                const input = this.refs.input

                console.log(input.value)
            }

            render() {
                const { name, sex, age } = this.props
                return (
                    <div>
                        // 这里使用ref定义, 类似id定义的值
                        <input ref="input"/>
                        <button onClick={this.showAlert}>点击显示</button>
                    </div>
                )
            }
        }
       

        ReactDOM.render(<Dom  />, document.getElementById('app1'))

    </script>
    
回调形式的ref, 字符串形式的ref不被推荐

<script type="text/babel">
        // 1. 创建class组件
        class Dom extends React.Component {


            showAlert = () => {
                console.log(this.input.value)
                // 也可以这么去取
                const { input } = this
            }

            render() {
                const { name, sex, age } = this.props
                return (
                    <div>
                        // 回调形式的ref
                        <input ref={ (currentNode) => this.input = currentNode }/>
                        <button onClick={this.showAlert}>点击显示</button>
                    </div>
                )
            }
        }
       

        ReactDOM.render(<Dom  />, document.getElementById('app1'))

    </script>
    
回调形式还是会有一个问题, 更新UI的时候会调用两次(重新渲染的时候会先释放掉, 然后渲染新的), 可以使用class的绑定函数, 来解决这个问题

<script type="text/babel">
        // 1. 创建class组件
        class Dom extends React.Component {


            showAlert = () => {
                const {input} = this
                console.log(input.value)
            }

            saveInput = (e) => {
                this.input = e
            }

            render() {
                const { name, sex, age } = this.props
                return (

                    <div>
                        
                    {/*
                        加{}表示括号里面的是要使用js表达式,  然后才能使用注释
                        <input ref={ (currentNode) => this.input = currentNode }/>
                    */}
                        <input ref={ this.saveInput }/>
                        <button onClick={this.showAlert}>点击显示</button>
                    </div>
                )
            }
        }
       

        ReactDOM.render(<Dom  />, document.getElementById('app1'))

    </script>

createRef() 应该是低于16都没有这个功能, 新的是使用useRef 
<script type="text/babel">
        // 1. 创建class组件
        class Dom extends React.Component {

            // 会返回一个容器, 就已经挂到了实例自身上
            // 这里面同时只能存在一份
            myRef = React.createRef()
            myRef2 = React.createRef()

            showAlert = () => {
                const {input} = this
                console.log(input.value)
            }

            showAert2 = () => {
                const {input2} = this
                console.log(input2.value)
            }

            render() {
                const { name, sex, age } = this.props
                return (

                    <div>
                        <input ref={ this.myRef }/>
                        <button onClick={this.showAlert}>点击显示</button>
                        <input onBlur={this.showAert2} ref={ this.myRef2 }/>
                    </div>
                )
            }
        }
       

        ReactDOM.render(<Dom  />, document.getElementById('app1'))

    </script>

image.png

注意: 不要过度使用ref, event.target可以获取到DOM控件