• React: JSX 、虚拟 DOM、组件配置(props、state、PropTypes、createContext、props.children)



    learn from 《React全家桶:前端开发与实例详解》
    https://zh-hans.reactjs.org/tutorial/tutorial.html
    https://zh-hans.reactjs.org/docs/create-a-new-react-app.html#create-react-app

    1. 虚拟 DOM

    我们操作虚拟DOM,让 React 负责更改浏览器的 DOM

    虚拟 DOM,指的是,表示实际 DOM 的 JavaScript 对象树

    开发人员只需要返回需要的 DOM,React 负责转换,且性能有优化,速度很快(高效的差异算法、更新子树、批量更新DOM)

    ReactElement 是 虚拟 DOM 中对 DOM 元素的表示

    先创建 RE,再 render (RE, 到实际的DOM挂载位置, 回调函数)

    2. JSX

    JSX 是 JavaScript Syntax Extension

    JSX可以很方便的编写 ReactElement(无状态,不可变) 层次结构

    babel 插件 可以 将 JSX 转译成 JS

    JSX 通常用 () 包含起来,JSX属性 用 {} 包含,
    JSX 内部注释 {/* 注释 */}

    JSX 使用 className 标识类

    JSX 不能使用 for 属性,而是 htmlFor

    3. ReactComponent

    ReactComponent 是一个 JS 对象,至少有一个 render() 函数,返回一个 ReactElement

    4. props 是参数

    props 是组件的输入

    props 可以传递任何 JS 对象

    • 基本类型、简单 JS 对象
    • 原子操作、函数、React元素、虚拟DOM节点

    5. PropTypes

    是验证 props 传递的值 的一种方法,属性名 : PropsTypes (string, number, boolean, function, object, array, arrayOf, node, element)

    import PropTypes from 'prop-types';
    import React from 'react';
    
    class DocumentedContainer extends React.Component {
      static propTypes = {
        children: PropTypes.oneOf([PropTypes.element, PropTypes.array])
      };
    
      render() {
        return <div className="container">{this.props.children}</div>;
      }
    }
    
    export default DocumentedContainer;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    6. getDefaultProps() 获取默认 props

    class Counter extends React.Component {
      static defaultProps = {
      		initialValue: 1
      	}
      	...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    使用 , 两种用法效果一致

    7. 上下文

    从 React 16.3.0 开始,可以指定通过组件树向下传递的变量,无需手动将变量从父组件传递到子组件

    • React.createContext 只接受一个参数,上下文提供的默认值

    相当于 全局公开 的属性

    例如网站主题:
    theme.js

    import React from 'react';
    
    export const themes = {
      light: {
        foreground: '#222222',
        background: '#e9e9e9'
      },
      dark: {
        foreground: '#fff',
        background: '#222222'
      }
    };
    
    export const ThemeContext = React.createContext(themes.dark);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在 app 中: ThemeContext.Provider 用于把数据传递给子组件

    import React, {Component} from 'react';
    
    import {ThemeContext, themes} from './theme';
    import './App.css';
    import Header from './Header';
    
    class App extends Component {
      state = {theme: themes.dark};
    
      changeTheme = evt => {
        this.setState(state => ({
          theme: state.theme === themes.dark ? themes.light : themes.dark
        }));
      };
    
      render() {
        return (
          <div className="App">
            <ThemeContext.Provider value={this.state.theme}>
              <Header />
              <p className="App-intro">
                To get started, edit <code>src/App.js</code> and save to reload.
              </p>
    
              <button onClick={this.changeTheme}>Change theme</button>
            </ThemeContext.Provider>
          </div>
        );
      }
    }
    
    export default App;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    Header.js ThemeContext.Consumer 从 Provider 父组件中获取主题

    import React from 'react';
    import logo from './logo.svg';
    
    import {ThemeContext} from './theme';
    
    export const Header = props => (
      <ThemeContext.Consumer>
        {theme => (
          <header
            className="App-header"
            style={{backgroundColor: theme.background}}
          >
            <img src={logo} className="App-logo" alt="logo" />
            <h1 className="App-title" style={{color: theme.foreground}}>
              Welcome to React
            </h1>
          </header>
        )}
      </ThemeContext.Consumer>
    );
    
    export default Header;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    8. 多个上下文

    user.js

    import React from 'react';
    export const UserContext = React.createContext(null);
    
    • 1
    • 2

    Body.js

    import React from 'react';
    
    import {ThemeContext} from './theme';
    import {UserContext} from './user';
    
    export const Body = props => (
        <ThemeContext.Consumer>
            {theme => (
                <header
                    className="App-header"
                    style={{backgroundColor: theme.background}}
                >
                    <UserContext.Consumer>
                        <h1>{user => (user ? 'Welcome back' : 'Welcome')}</h1>
                    </UserContext.Consumer>
                </header>
            )}
        </ThemeContext.Consumer>
    );
    
    export default Body;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    9. state

    Switch.css

    .active {
      background-color: #d5fffb;
      font-weight: bold;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    支付选项:

    import React from "react";
    import "../Switch.css";  // 导入样式
    
    const CREDITCARD = "Creditcard";
    const BTC = "Bitcoin";
    
    class Switch extends React.Component {
        state = {
            payMethod: BTC // 默认支付选项
        };
    
        select = choice => {
            return evt => {
                this.setState({
                    payMethod: choice
                }); // 返回一个函数
            };
        };
    
        renderChoice = choice => {
            // create a set of cssClasses to apply
            const cssClasses = ["choice"];  // 样式选项
    
            if (this.state.payMethod === choice) {
                cssClasses.push("active"); // add .active class
            } // 支付的选项样式,增加一个 active 样式
    
            return (
                <div className={cssClasses.join(" ")} onClick={this.select(choice)}>
                    {choice}  
                </div>
            ); // 把样式渲染给 choice 
        };
    
        render() {
            return (
                <div className="switch">
                    {this.renderChoice(CREDITCARD)}
                    {this.renderChoice(BTC)}
                    Pay with: {this.state.payMethod}
                </div>
            );
        }
    }
    
    export default Switch;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    在setState中使用函数,而不是对象

    为什么?下面是一个点击减少的按钮

    使用对象的方式赋值给 state,如果用户点击过快,计算机非常慢,而 setState 是异步的,如果碰到更高优先级的响应过载,这个减少按钮的点击响应还在队列中等待,那么用户可能点了3次,但是最后数值只减少了1

    • 状态转换依赖于当前状态时,最好使用函数来设置状态,避免这种Bug
      decrement = () => {
        // Appears correct, but there is a better way
        const nextValue = this.state.value - 1;
        this.setState({
          value: nextValue
        });
      };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    更好的写法如下:

      decrement = () => {
        this.setState(prevState => {
          return {
            value: prevState.value - 1
          };
        });
      };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 通常在组件里存的状态越少越好,最好是从外部获取,状态多了,会使得系统的状态是什么样子的变得难以推理
    • 可以使用多个无状态组件构成 一个有状态组件

    10. 无状态组件

    • React 中 只需要 render() 方法的组件

    无状态组件,它不是一个类,我们不会引用 this
    这种函数式组件,性能更好

    const Header = function(props) {
      return (<h1>{props.headerText}</h1>)
    }
    
    • 1
    • 2
    • 3

    有状态

    export class Header extends React.Component {
      render() {
        return (
          <h1>{this.props.headerText}</h1>
        );
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    11. 使用 props.children 与子组件对话

    可以使用 this.props.children 引用树中的子组件

    import PropTypes from 'prop-types';
    import React from 'react';
    
    class DocumentedContainer extends React.Component {
      static propTypes = {
        children: PropTypes.oneOf([PropTypes.element, PropTypes.array])
      };
    
      render() {
        return <div className="container">{this.props.children}</div>;
      }
    }
    
    export default DocumentedContainer;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    处理子组件 :

    • map(),返回调用函数的结果的数组
    • forEach() 不收集结果
    import PropTypes from 'prop-types';
    import React from 'react';
    
    class MultiChildContainer extends React.Component {
      static propTypes = {
        component: PropTypes.element.isRequired,
        children: PropTypes.element.isRequired
      };
    
      renderChild = (childData, index) => {
        return React.createElement(
          this.props.component,
          {}, // <~ child props
          childData // <~ child's children
        );
      };
    
      render() {
        return (
          <div className="container">
            {React.Children.map(this.props.children, this.renderChild)}
          </div>
        );
      }
    }
    
    export default MultiChildContainer;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • React.Children.toArray() 函数 转成子元素的数组
    import PropTypes from 'prop-types';
    import React from 'react';
    
    class ArrayContainer extends React.Component {
      static propTypes = {
        component: PropTypes.element.isRequired,
        children: PropTypes.element.isRequired
      };
    
      render() {
        const arr = React.Children.toArray(this.props.children);
    
        return <div className="container">{arr.sort((a, b) => a.id < b.id)}</div>;
      }
    }
    
    export default ArrayContainer;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
  • 相关阅读:
    干货 | 携程数据基础平台2.0建设,多机房架构下的演进
    解决js代码中加入alert就成功执行请求接口,不加就不执行的问题
    网络协议--TCP的成块数据流
    新版IDEA没有办法选择Java8版本解决方法
    通过vuex存储token,通过前置路由守卫完成对登录操作之后的token值验证,完成登录状态的保持
    简述 HTML 的语义化标签是什么?
    Python学习基础笔记十五——命名空间和作用域
    mysql日志(错误日志、binlog日志、查询日志、慢日志)
    Kafka的数据可靠与数据重复
    OpenCV:python图像旋转,cv2.getRotationMatrix2D 和 cv2.warpAffine 函数
  • 原文地址:https://blog.csdn.net/qq_21201267/article/details/125954369