美文网首页前端开发那些事儿
快来跟我一起学 React(Day5)

快来跟我一起学 React(Day5)

作者: vv_小虫虫 | 来源:发表于2021-03-22 21:37 被阅读0次

    简介

    上一节我们完成了从 0 开始搭建一个企业级 React 项目的全部内容,项目是有了,但是我们一直都没有近距离接触过 React,所以接下来我们就快速撸一遍 React 官方文档内容,弄清楚一些概念性的东西,为后面的源码分析章节做铺垫。

    知识点

    • 项目搭建
    • 核心概念
    • 高级指引
    • API 指引
    • hook 指引

    后面这几节都比较轻松,因为我们基本上把 React 官网:https://reactjs.org/ 的内容跑一遍。

    让我们开始吧!

    项目搭建

    我们直接 clone 一个前面我们搭建的基础项目,然后取名字为 react-demo-day5

    git clone https://gitee.com/vv_bug/cus-react-demo.git react-demo-day5
    

    接着我们打开 react-demo-day5 目录,并且安装 npm 依赖:

    cd react-demo-day5 && npm install --registry https://registry.npm.taobao.org
    

    然后我们在 react-demo-day5 目录下执行 npm start 命令启动项目:

    npm start
    

    启动项目后,浏览器会自动打开我们项目的入口页面:

    1-1.png

    到这,我们的准备工作就算是完成了。

    组件 & Props

    组件,从概念上类似于 JavaScript 函数。它接受任意的入参(即 “props”),并返回用于描述页面展示内容的 React 元素。

    React 中有 “函数式” 与 ”类组件“ 之分,下面我们就通过 Demo 来演示一下。

    在开始之前,我们先修改一下当前项目结构。

    首先修改一下 src/main.tsx 文件:

    import React from "react";
    import ReactDOM from "react-dom";
    import "./main.scss";
    import MainConcepts from "./main-concepts";
    // App 组件
    const App = (
        <div className="root">
            {/* 核心概念 */}
            <MainConcepts/>
        </div>
    );
    ReactDOM.render(
        App,
        document.getElementById("root")
    );
    
    

    可以看到,我们抽离了一个 App 组件实例,然后在 App 中引入了 MainConcepts 组件。

    接下来我们在 src 目录中创建一个 main-concepts 目录,然后在 src/main-concepts 目录下创建一个 index.tsx 文件:

    mkdir ./src/main-concepts && touch ./src/main-concepts/index.tsx
    

    然后将以下内容写入到 src/main-concepts/index.tsx 文件:

    import ComponentsAndProps from "./components-and-props";
    
    /**
     * 核心概念列表组件
     */
    function mainConcepts() {
        return (
            <div>
                {/* 组件与属性 */}
                <ComponentsAndProps/>
            </div>
        );
    };
    export default mainConcepts;
    

    接着在 src/main-concepts 目录下创建一个 components-and-props 目录,并在 components-and-props 目录下创建一个 index.tsx 文件:

    mkdir ./src/main-concepts/components-and-props && touch ./src/main-concepts/components-and-props/index.tsx
    

    然后将以下内容写入到 src/main-concepts/components-and-props/index.tsx 文件:

    import React from "react";
    import WelcomeCom from "./welcome.com";
    import WelcomeFunc from "./welcome.func";
    
    function componentsAndProps() {
        return (
            <React.Fragment>
                {/* 类组件 */}
                <WelcomeCom/>
                {/* 函数式组件 */}
                <WelcomeFunc/>
            </React.Fragment>
        );
    };
    export default componentsAndProps;
    

    类组件

    继续在 src/main-concepts/components-and-props 下创建一个 welcome.com.tsx 文件作为类组件:

    touch ./src/main-concepts/components-and-props/welcome.com.tsx
    

    然后将以下内容写入到 src/main-concepts/components-and-props/welcome.com.tsx 组件:

    import React from "react";
    import PropTypes from "prop-types";
    type Prop = {
        name: string, // 姓名
    };
    class Welcome extends React.Component<Prop> {
        static propTypes = {
            name: PropTypes.string,
        };
        static defaultProps = {
            name: "小虫"
        };
    
        render() {
            return <h1>我是类组件,Hello, {this.props.name}</h1>;
        }
    }
    export default Welcome;
    

    可以看到,我们用类组件方式定义了一个 Welcome 组件,然后在 Welcome 组件中定义了一个 name 属性,并且利用 tsprop-types 对属性进行了校验,一个简单的 “React 类组件” 就创建完成了。

    函数式组件

    同样在src/main-concepts/components-and-props 下创建一个 welcome.func.tsx 文件作为函数式组件:

    touch ./src/main-concepts/components-and-props/welcome.func.tsx
    

    然后将以下内容写入到 src/main-concepts/components-and-props/welcome.func.tsx 组件:

    import PropTypes from "prop-types";
    type Prop = {
        name: string, // 姓名
    };
    function Welcome(props: Prop) {
        return <h1>我是函数式组件,Hello, {props.name}</h1>;
    }
    Welcome.propTypes={
        name: PropTypes.string
    };
    Welcome.defaultProps = {
        name: "小虫"
    };
    export default Welcome;
    

    可以看到,我们用函数式组件方式定义了一个 Welcome 组件,然后在 Welcome 组件中定义了一个 name 属性,并且利用 tsprop-types 对属性进行了校验,一个简单的 “React 函数式组件” 就创建完成了。

    运行

    react-demo-day5 项目根目录下执行 npm start 命令重新启动项目:

    npm start
    
    1-2.png

    可以看到,两个组件都正常显示到了页面。

    组合组件

    组件可以在其输出中引用其他组件。这就可以让我们用同一组件来抽象出任意层次的细节。按钮,表单,对话框,甚至整个屏幕的内容:在 React 应用程序中,这些通常都会以组件的形式表示。

    例如,我们的 src/main-concepts/components-and-props/index.tsx 组件:

    import React from "react";
    import WelcomeCom from "./welcome.com";
    import WelcomeFunc from "./welcome.func";
    
    function componentsAndProps() {
        return (
            <React.Fragment>
                {/* 类组件 */}
                <WelcomeCom/>
                {/* 函数式组件 */}
                <WelcomeFunc/>
            </React.Fragment>
        );
    };
    export default componentsAndProps;
    

    我们把 “函数式组件” 跟 “类组件” 组合到了一个组件中。

    Props 的只读性

    React 中,组件决不能修改自身的 props。

    React 非常灵活,但它也有一个严格的规则:

    所有 React 组件都必须像纯函数一样保护它们的 props 不被更改。

    我们可以试一下,比如我们修改一下 src/main-concepts/components-and-props/welcome.com.tsx 文件:

    import React from "react";
    import PropTypes from "prop-types";
    
    type Prop = {
        name: string, // 姓名
    };
    
    class Welcome extends React.Component<Prop> {
        static propTypes = {
            name: PropTypes.string,
        };
        static defaultProps = {
            name: "小虫"
        };
    
        render() {
            console.log(Object.isFrozen(this.props));
            this.props.name = "小虫虫";
            return <h1>我是类组件,Hello, {this.props.name}</h1>;
        }
    }
    
    export default Welcome;
    

    可以看到,我们在 render 方法中试着去修改 name 属性值,并且我们打印了 this.props 是否是 Object.freeze 类型:

    console.log(Object.isFrozen(this.props));
    this.props.name = "小虫虫";
    

    我们保存文件等自动编译完成:


    1-3.png

    可以看到,三处报错了:

    1. Webpack 编译直接报错了,说 “我们不能修改只读属性”。
    2. IDE 也报错了,主要是 Eslint 的配置。
    3. 浏览器也报错了,说 “遇到了未知异常”。
    4. Object.isFrozen(this.props) 返回了 true

    从上面可以看出,我们利用了 TypeScriptEslint 等规则在写代码的时候就已经成功避免了这类错误的出现,最后 ReactJs 还会直接渲染报错,因为我们对一个 Object.freeze 类型的对象进行了修改操作。

    当然,即使有各种条件的限制,但是我们还是可以变相的去修改 props的值,比如我们把一个属性定义为 object 类型,我们还是可以在子组件中修改这个属性的某些值,虽然我们可以这样做,但是在开发的时候千万不要这么干哈,因为在某些大项目中,当进行变量追踪的时候,你压根就不知道是谁修改了这个属性的内容,这样就很容易出错了, 我就不演示了。

    State & 生命周期

    State

    State 相当于 MVVM 模式中的 ViewModel,通过监听对比 ViewModel 的变化,最后实现页面的更新,每个组件都可以定义自己的 state

    我们在 src/main-concepts 目录下创建一个 state-and-lifecycle 目录:

    mkdir ./src/main-concepts/state-and-lifecycle
    

    然后在 /src/main-concepts/state-and-lifecycle 中创建一个 index.tsx 文件:

    import React from "react";
    import StateComponent from "./state.com";
    import StateFunc from "./state.func";
    
    function stateAndLifecycle() {
        return (
            <React.Fragment>
                {/* 类组件带 state */}
                <StateComponent/>
                {/* 函数组件带 state */}
                <StateFunc/>
            </React.Fragment>
        );
    };
    export default stateAndLifecycle;
    

    类组件带 State

    /src/main-concepts/state-and-lifecycle 中创建一个 state.com.tsx 文件:

    import React from "react";
    
    type State = {
        status: boolean
    };
    
    class StateComponent extends React.Component<any, State> {
        state = {
            status: true
        }
    
        render() {
            return (
                <div
                    onClick={this.onToggle.bind(this)}
                >
                    我是类组件:{this.state.status ? "on" : "off"}
                </div>
            );
        }
    
        /**
         * 切换状态
         */
        onToggle() {
            // 修改 status 状态
            this.setState((state) => {
                return {
                    status: !state.status
                };
            });
        }
    }
    
    export default StateComponent;
    

    可以看到,我们在类组件 state.com.tsx 中定义了一个 state,然后给 div 元素添加了一个点击事件,最后在点击事件 onToggle 回调中用 setState 修改了 status 的值。

    函数组件带 State

    /src/main-concepts/state-and-lifecycle 中创建一个 state.func.tsx 文件:

    import React, {useState} from "react";
    
    function StateFunc() {
        let [status, setStatus] = useState<boolean>(true);
    
        function onToggle() {
            setStatus(!status);
        }
    
        return (
            <div
                onClick={onToggle}
            >
                我是函数组件:{status ? "on" : "off"}
            </div>
        );
    }
    
    export default StateFunc;
    

    可以看到,我们直接利用了 useState 这个 Hook 定义了一个 state,跟上面的类组件一样,在点击事件中修改了 status 的值,之前说函数式组件是 “无状态的”,但是利用了 Hook,我们同样是可以让一个函数式组件也具备 StateHook 的内容我们后面再详细解析。

    我们保存等项目重新编译看结果:

    1-4.png

    当我们点击对应文字区域的时候,页面会进行 onoff 的切换效果,我就不演示了哈,小伙伴自己试试。

    正确地使用 State

    • 不要直接修改 State
    • State 的更新可能是异步的
    • State 的更新会被合并

    生命周期

    先上一张官方提供的 React 的生命周期图:

    1-5.png

    图片来源:https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/

    生命周期我们后期源码解析的时候再详细讲解。

    事件处理

    React 元素的事件处理和 DOM 元素的很相似,但是有一点语法上的不同:

    • React 事件的命名采用小驼峰式(camelCase),而不是纯小写。
    • 使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。

    例如,传统的 HTML:

    <button onclick="activateLasers()">
      Activate Lasers
    </button>
    

    在 React 中略微不同:

    <button onClick={activateLasers}>  
      Activate Lasers
    </button>
    

    在 React 中另一个不同点是你不能通过返回 false 的方式阻止默认行为。你必须显式的使用 preventDefault 。例如,传统的 HTML 中阻止链接默认打开一个新页面,你可以这样写:

    <a href="#" onclick="console.log('The link was clicked.'); return false">
      Click me
    </a>
    

    在 React 中,可能是这样的:

    function ActionLink() {
      function handleClick(e) {    
            e.preventDefault();    
            console.log('The link was clicked.');  
        }
      return (
        <a href="#" onClick={handleClick}>      
          Click me
        </a>
      );
    }
    

    因为 eReact 生成的一个合成事件,React 事件与原生事件不完全相同。

    上面例子中有演示过的,就不再演示了。

    条件渲染

    因为 React 中可以使用 JSX 语法,所以我们可以在 JSX 语法中进行条件判断做渲染就可以了。

    元素变量

    你可以使用变量来储存元素。 它可以帮助你有条件地渲染组件的一部分,而其他的渲染部分并不会因此而改变。

    我们还是来演示一下吧。

    首先在 src/main-concepts 目录下创建一个 condition-render 目录:

    mkdir ./src/main-concepts/condition-render
    

    然后在 src/main-concepts/condition-render 目录下创建一个 index.tsx 文件:

    import React from "react";
    import ConditionFunc from "./condition.func";
    
    function stateAndLifecycle() {
        return (
            <React.Fragment>
                {/* 函数式组件带条件渲染 */}
                <ConditionFunc/>
            </React.Fragment>
        );
    };
    export default stateAndLifecycle;
    

    接着在 src/main-concepts/condition-render 目录下创建一个 condition.func.tsx 文件:

    import React, {useState} from "react";
    
    function ConditionFunc() {
        let [isLoggedIn, setLogged] = useState<boolean>(true);
    
        function handleLogin() {
            setLogged(true);
        }
    
        function handleLogout() {
            setLogged(false);
        }
    
        let button = null;
        if (isLoggedIn) {
            button = (
                <button onClick={handleLogout}>退出登录</button>
            );
        } else {
            button = (
                <button onClick={handleLogin}>去登录</button>
            );
        }
    
        return (
            <div>
                {isLoggedIn && "恭喜,登录成功!"}
                {button}
            </div>
        );
    }
    
    export default ConditionFunc;
    

    可以看到,我们利用 button 变量充当了一个元素,然后通过 StateisLoggedIn 变量进行条件判断,对 button 变量进行赋值。

    最后我们在 src/main-concepts/index.tsx 文件中引入 src/main-concepts/condition-render/index.tsx 组件测试:

    1-6.gif

    可以看到,页面中根据我们的点击条件渲染了不同的状态。

    与运算符 &&

    通过花括号包裹代码,你可以在 JSX 中嵌入表达式。这也包括 JavaScript 中的逻辑与 (&&) 运算符。它可以很方便地进行元素的条件渲染。

    比如上面的condition.func.tsx 文件,我们用 “运算符 &&” 方式来改造一下:

    import React, {useState} from "react";
    
    function ConditionFunc() {
        let [isLoggedIn, setLogged] = useState<boolean>(true);
    
        function handleLogin() {
            setLogged(true);
        }
    
        function handleLogout() {
            setLogged(false);
        }
    
        return (
            <div>
                {isLoggedIn && "恭喜,登录成功!"}
                {isLoggedIn && (<button onClick={handleLogout}>退出登录</button>)}
                {!isLoggedIn && ( <button onClick={handleLogin}>去登录</button>)}
            </div>
        );
    }
    
    export default ConditionFunc;
    

    三目运算符

    另一种内联条件渲染的方法是使用 JavaScript 中的三目运算符 condition ? true : false

    比如上面的condition.func.tsx 文件,我们用 “三目运算符” 方式来改造一下:

    import React, {useState} from "react";
    
    function ConditionFunc() {
        let [isLoggedIn, setLogged] = useState<boolean>(true);
    
        function handleLogin() {
            setLogged(true);
        }
    
        function handleLogout() {
            setLogged(false);
        }
    
        return (
            <div>
                {
                    isLoggedIn ? "恭喜,登录成功!" : ""
                }
                {
                    isLoggedIn ? (
                        <button onClick={handleLogout}>退出登录</button>
                    ) : (
                        <button onClick={handleLogin}>去登录</button>
                    )
                }
            </div>
        );
    }
    
    export default ConditionFunc;
    

    后面两种效果跟第一种一样,我就不演示了。

    不过在平时的项目开发中,面对复杂一点的逻辑判断,不建议用后两种内联方式,因为对代码的可读性跟调试都不友好。

    列表 & Key

    在 React 中,我们只需要把数组转化为元素列表就可以了。

    我们来演示一下。

    元素变量数组

    首先一样的套路,在 src/main-concepts 目录下创建一个 list-and-key 目录:

    mkdir ./src/main-concepts/list-and-key
    

    然后在 src/main-concepts/list-and-key 目录下创建一个 index.tsx 文件:

    import React from "react";
    import ListFunc from "./list.func";
    
    function ListAndKey() {
        return (
            <React.Fragment>
                {/* 函数组件列表渲染 */}
                <ListFunc/>
            </React.Fragment>
        );
    };
    export default ListAndKey;
    

    接着在 src/main-concepts/list-and-key 下创建一个 list.func.tsx 文件:

    import React, {useState} from "react";
    
    function ListFunc() {
        let [todos] = useState<Array<string>>(["React", "Vue", "Angular"]);
        let todoElements = todos.map((todo) => (<li>{todo}</li>));
        return (
            <ul>
                {todoElements}
            </ul>
        );
    }
    
    export default ListFunc;
    

    可以看到,我们用了一个元素数组 todoElements 变量来承载了我们所有需要渲染的元素,最后利用 JSX 语法渲染。

    最后在 src/main-concepts/index.tsx 中引入 src/main-concepts/list-and-key/index.tsx 组件:

    import ComponentsAndProps from "./components-and-props";
    import StateAndLifecycle from "./state-and-lifecycle";
    import ConditionRender from "./condition-render";
    import ListAndKey from "./list-and-key";
    
    /**
     * 核心概念列表组件
     */
    function mainConcepts() {
        return (
            <div>
                {/* 组件与属性 */}
                <ComponentsAndProps/>
                {/* State & 生命周期 */}
                <StateAndLifecycle/>
                {/* 条件渲染 */}
                <ConditionRender/>
                {/* 列表与 key */}
                <ListAndKey/>
            </div>
        );
    };
    export default mainConcepts;
    

    我们重新运行项目看效果:

    npm start
    
    1-7.png

    可以看到,页面中正常渲染了我们的 todos 列表。

    在 JSX 中嵌入 map()

    我们可以直接把 map 放在 JSX 语法中。

    比如我们重构一下上面的 list.func.tsx 组件:

    import React, {useState} from "react";
    
    function ListFunc() {
        let [todos] = useState<Array<string>>(["React", "Vue", "Angular"]);
        return (
            <ul>
                {todos.map((todo) => (<li>{todo}</li>))}
            </ul>
        );
    }
    
    export default ListFunc;
    

    效果跟上面的一样,我就不演示了。

    不过还是那句话,简单的逻辑可以用 JSX 内联语法操作,复杂的逻辑就不建议用内联了,对调试跟代码的可读性都不友好。

    key

    key 帮助 React 识别哪些元素改变了,比如被添加或删除。因此你应当给数组中的每一个元素赋予一个确定的标识。

    我们现在没有提供 key,在开发模式中会报错:

    1-10.png

    我们需要修改一下 list.func.tsx 组件:

    import React, {useState} from "react";
    
    function ListFunc() {
        let [todos] = useState<Array<string>>(["React", "Vue", "Angular"]);
        return (
            <ul>
                {todos.map((todo) => (<li key={todo}>{todo}</li>))}
            </ul>
        );
    }
    
    export default ListFunc;
    

    可以看到,我们给每一个 li 标签添加了一个 key 属性(数组元素中使用的 key 在其兄弟节点之间应该是独一无二的。然而,它们不需要是全局唯一的)。

    表单

    受控组件

    输入的值始终又 ReactState 控制的组件就叫 “受控组件”。

    我们来演示一下。

    首先在 src/main-concepts 目录下创建一个 form 目录:

    mkdir ./src/main-concepts/form
    

    接着在 src/main-concepts/form 目录中创建一个 index.tsx 文件:

    import React from "react";
    import ControlledFunc from "./controlled.func";
    
    function Form() {
        return (
            <React.Fragment>
                {/* 函数组件之受控组件 */}
                <ControlledFunc/>
            </React.Fragment>
        );
    };
    export default Form;
    

    然后在 src/main-concepts/form 目录中创建一个 controlled.func.tsx 文件:

    import React, {useState} from "react";
    
    function ControlledFunc() {
        let [name, setName] = useState<string>("");
    
        function handleInput(event: any) {
            setName(event.target.value);
        }
    
        return (
            <div>
                <input value={name} onInput={handleInput}/>
                <div>{name}</div>
            </div>
        );
    }
    
    export default ControlledFunc;
    

    可以看到,我们用了一个 Statename 的属性值,通过监听 input 标签的 onInput 事件,然后把输入的值赋给了 name 变量,最后 Statename 变量又控制着 input 的输入值,这样一个受控组件就创建完毕了。

    接着我们在 src/main-concepts/index.tsx 组件中引入 src/main-concepts/form/index.tsx 组件:

    import ComponentsAndProps from "./components-and-props";
    import StateAndLifecycle from "./state-and-lifecycle";
    import ConditionRender from "./condition-render";
    import ListAndKey from "./list-and-key";
    import Form from "./form";
    
    /**
     * 核心概念列表组件
     */
    function mainConcepts() {
        return (
            <div>
                {/* 组件与属性 */}
                <ComponentsAndProps/>
                {/* State & 生命周期 */}
                <StateAndLifecycle/>
                {/* 条件渲染 */}
                <ConditionRender/>
                {/* 列表与 key */}
                <ListAndKey/>
                {/* 表单-受控组件 */}
                <Form/>
            </div>
        );
    };
    export default mainConcepts;
    

    我们重新运行 npm start 命令开启项目看结果:

    npm start
    
    1-8.gif

    可以看到,当我们输入的时候,State 中的 name 变量实时跟 input 输入的值绑定。

    textareaselect 等其它的 form 标签也可以进行同样的操作,就不一一演示了。

    状态提升

    通常,state 都是首先添加到需要渲染数据的组件中去,如果其他组件也需要这个 state,那么你可以将它提升至这些组件的最近共同父组件中,这种操作就叫 “状态提升”。

    我们还是通过 Demo 来演示一下吧。

    我们首先在 src/main-concepts 目录下创建一个 lifting-state-up 目录:

    mkdir ./src/main-concepts/lifting-state-up
    

    然后在 src/main-concepts/lifting-state-up 目录下创建一个 index.tsx 文件:

    import React, {useState} from "react";
    import StateUpCom from "./state-up.com";
    
    function LiftingStateUp() {
      let [price, setPrice] = useState(0);
      let [count, setCount] = useState(0);
    
      /**
       * 处理单价
       */
      function handlePriceInput(event: any) {
        setPrice(parseFloat(event.target.value));
      }
    
      /**
       * 处理数量
       */
      function handleCountInput(event: any) {
        setCount(parseFloat(event.target.value));
      }
    
      // 计算总价
      let total = count * price;
      return (
    
        <div>
          {/* 状态提示--价格 */ }
          <StateUpCom title={ "价格:" } value={price} handleInput={ handlePriceInput }/>
          {/* 状态提示--数量 */ }
          <StateUpCom title={ "数量:" } value={count} handleInput={ handleCountInput }/>
          总价:{ total }
        </div>
      );
    }
    export default LiftingStateUp;
    

    接着在 src/main-concepts/lifting-state-up 目录下创建一个 state-up.com.tsx 组件:

    import React from "react";
    import PropTypes from "prop-types";
    
    type HandleInputFunc = (event: any) => void;
    type Prop = {
      title: string,
      value: number,
      handleInput: HandleInputFunc,
    };
    
    class StateUpCom extends React.Component<Prop> {
      static propTypes = {
        title: PropTypes.string, // 标题
        value: PropTypes.number, // 输入值
        handleInput: PropTypes.func, // 处理输入监听函数
      }
      static defaultProps = {
        title: "",
        value: 0
      }
    
      render() {
        const {title, value, handleInput} = this.props;
        return (
          <fieldset>
            <legend>{title}</legend>
            <input onInput={handleInput} type="number" value={value}/>
          </fieldset>
        );
      }
    }
    
    export default StateUpCom;
    

    可以看到,我们把 StateUpCom 组件的 input 输入值通过handleInput 提升到了 “父组件” lifting-state-up/index.tsx

    最后我们在 src/main-concepts/index.tsx 组件中引入 src/main-concepts/lifting-state-up/index.tsx 组件:

    import ComponentsAndProps from "./components-and-props";
    import StateAndLifecycle from "./state-and-lifecycle";
    import ConditionRender from "./condition-render";
    import ListAndKey from "./list-and-key";
    import Form from "./form";
    import LiftingStateUp from "./lifting-state-up";
    
    /**
     * 核心概念列表组件
     */
    function mainConcepts() {
        return (
            <div>
                {/* 组件与属性 */}
                <ComponentsAndProps/>
                {/* State & 生命周期 */}
                <StateAndLifecycle/>
                {/* 条件渲染 */}
                <ConditionRender/>
                {/* 列表与 key */}
                <ListAndKey/>
                {/* 表单-受控组件 */}
                <Form/>
                {/* 状态提升 */}
                <LiftingStateUp/>
            </div>
        );
    };
    export default mainConcepts;
    

    我们重新运行项目看结果:

    npm start
    
    1-9.gif

    可以看到,子组件中的输入值都提升到了父组件,父组件会根据子组件中的输入值自动算出总价的值。

    总结

    我们照着 React 官网:https://reactjs.org/ 的内容跑了一遍 React 的所有核心概念,虽然有些概念可能很简单,但是搞技术的切勿眼高手低,有些看似很简单的东西,看千遍不如自己敲一遍,弄清这些概念对我们后面分析 React 的源码很有帮助,后面我们还会对 React 的高级特性以及一些 API 做解析。

    ok,这节就先到这了,下节见!

    本节内容的 Demo 项目地址:https://gitee.com/vv_bug/react-demo-day5/tree/dev/

    相关文章

      网友评论

        本文标题:快来跟我一起学 React(Day5)

        本文链接:https://www.haomeiwen.com/subject/zvvthltx.html