React入门案例 - 购物车计数

753 阅读4分钟

开始学习react,先从react官网的案例开始,,学习react中的组件,数据传递,还有JSX

项目地址

预备知识

react组件的 render 方法

  • 必须要有render
  • 外层仅有一个div
  • {} 内可以放任何JavaScript代码,如表达式,函数等
  • class 用 className
  • label的for属性 用 htmlFor

MDN - react入门

  • 所有的React模块都要引用React(import React from 'react'),是为了将代码中的JSX转为React.createElement()
  • React 组件使用帕斯卡命名法,又称为"大驼峰式命名法",如“HelloWorld”
  • React 的组件要用export导出,这样其他文件可以使用
  • prop 是任何传入 React 组件的数据
  • 通过把变量放在大括号中,您可以读取 JSX 的变量,如{so}

react里的 index.js 是怎么跟 index.html 结合起来的?

  • 你可以在项目下运行npm run eject,被隐藏的配置文件就会暴露到项目根路径下。把请求转发到index.html原因是,你执行npm run start时,启动的webpack-dev-server,会加载react-script项目config文件夹下的配置(paths.js),里面定义了请求的默认转发路径是public文件夹,自然就找到了public下的index.html。

案例分析

视图结构

image.png

文件结构

  • src
    • components - 组件
      • counter.jsx
      • counters.jsx
      • navbar.jsx
    • App.js - 组件集合
    • index.css - 全局样式
    • index.js - 文件入口

代码细节

index.js

index.js主要作用

  • 把App组件渲染到页面中,
  • 使用的是ReactDOM,把App组件的内容,放到root里!
  • 引入全局样式,这样组件就能使用了

image.png

App.js

import React, { Component } from "react"; // 必须要引入React!
import NavBar from "./components/navbar"; // 引入组件
import Counters from "./components/counters";

class App extends Component {
  state = { // 定义数据集
    counters: [
      { id: 1, value: 0 },
      { id: 2, value: 0 },
      { id: 3, value: 0 },
      { id: 4, value: 0 },
    ],
  };

  handleIncrement = (counter) => { // 父组件定义方法 - 增加数量,用于向子组件传递!
    const counters = [...this.state.counters]; // 数据备份,不能直接修改state中的内容!
    const index = counters.indexOf(counter);
    // counters[index] = { ...counters[index] }; // 这一步是????
    counters[index].value++;
    this.setState({ counters }); // 使用set函数,视图重新 render 渲染
  };

  handleDecrement = (counter) => {
  };

  handleReset = () => {
  };

  handleDelete = (counterId) => {
  };

  handleRestart = () => {
  };

  render() { // render函数!
    return ( // 返回一个jsx!外层仅一个div
      <div className="main__wrap">
        <main className="container">
          <div className="card__box">
            <NavBar
              totalCounters={
                this.state.counters.filter((c) => c.value > 0).length
              }
            />
            <Counters
              counters={this.state.counters} // 这里的this指的是App向子组件传递方法onReset={this.handleReset}
              onIncrement={this.handleIncrement}
              onDecrement={this.handleDecrement}
              onDelete={this.handleDelete}
              onRestart={this.handleRestart}
            />
          </div>
        </main>
      </div>
    );
  }
}
export default App; // 导出App组件

counters.jsx

React官网 - Lists and Keys

  • 学习渲染组件的时候要加入key,vue的key要加冒号,react不用。。
  • 使用map来循环渲染组件!
  • key可以帮助react决定那些是要改变的,添加的,删除的,
  • key应该放在map函数里面,这样保证每个都是key都是唯一的。。(推荐不要用index做key)
  • 兄弟节点直接key要唯一,这个key不用全局唯一
  • 如果map函数内容很多很多,就该抽离成组件了!
import React, { Component } from "react"; // 必须要引入React
import Counter from "./counter";

class Counters extends Component {
  render() {
    // 接收传入数据。
    const { onReset, onIncrement, onDelete, onDecrement, counters, onRestart } =
      this.props;

    return ( // 返回JSX
      <div>
        {/* 两个按钮 */}
        <div className="row">
          <div className="">
            <button
              className="btn btn-success m-2"
              onClick={onReset} // 使用传入方法
              disabled={counters.length === 0 ? "disabled" : ""}
            >
              <i className="fa fa-refresh" aria-hidden="true" /> // bootstrap的icon
            </button>

            <button
              className="btn btn-primary m-2"
              onClick={onRestart}
              disabled={counters.length !== 0 ? "disabled" : ""}
            >
              <i className="fa fa-recycle" aria-hidden="true" />
            </button>
          </div>
        </div>

        {/* map遍历 */}
        {counters.map((counter) => (
          // 继续传递
          <Counter
            key={counter.id} // 在map里保证key的唯一
            counter={counter} // 继续向子组件传递
            onIncrement={onIncrement}
            onDecrement={onDecrement}
            onDelete={onDelete}
          />
        ))}
      </div>
    );
  }
}

export default Counters;

counter.jsx

// 为什么这里的React 不能去掉???

// 第一句代码引入了 React 库,这是为了将代码中的 JSX 语句转为React.createElement(),
// 所有的 React 模块都应该引入 React 模块,否则会抛错。
import React, { Component } from "react";

class Counter extends Component {
  render() {
    return (
      <div>
        <div className="row">
          <div className="">
            {/* 颜色变换 + 数值变换 */}
            <span style={{ fontSize: 24 }} className={this.getBadgeClasses()}>
              {this.formatCount()} // 通过方法的返回值来显示文字信息
            </span>
          </div>

          <div className="">
            <button
              className="btn btn-secondary"
              // 使用传入的方法增加函数),这里传入的参数用于在App.js中判断。。。。
              onClick={() => this.props.onIncrement(this.props.counter)} 
            >
              <i className="fa fa-plus-circle" aria-hidden="true" />
            </button>
            <button
              className="btn btn-info m-2"
              onClick={() => this.props.onDecrement(this.props.counter)}
              disabled={this.props.counter.value === 0 ? "disabled" : ""}
            >
              <i className="fa fa-minus-circle" aria-hidden="true" />
            </button>
            <button
              className="btn btn-danger"
              // 这里的删除使用的是filter过虑把这个传入的id筛掉
              onClick={() => this.props.onDelete(this.props.counter.id)}
            >
              <i className="fa fa-trash-o" aria-hidden="true" />
            </button>
          </div>
        </div>
      </div>
    );
  }
    
  // 返回class内容。。
  getBadgeClasses = () => {
    let classes = "badge m-2 badge-";
    classes += this.props.counter.value === 0 ? "warning" : "primary";
    return classes;
  };
   
  // 返回文字信息
  formatCount = () => {
    const { value } = this.props.counter;
    return value === 0 ? "Zero" : value;
  };
}

export default Counter;

navbar.jsx

发现还可以直接用函数接收变量。。

import React from "react";

// Stateless Functional Component

// 这里不用props,直接使用App.js传入的参数。。?????
const NavBar = ({ totalCounters }) => {

// 这里直接返回JSX就可以了。。不用render函数,如果基础component,必须要render函数
  return (
    <nav className="navbar navbar-light">
      <div className="navbar-brand">
        <i className="fa fa-shopping-cart fa-lg m-2" aria-hidden="true" />
        <span
          className="badge badge-pill badge-info m-2"
          style={{ width: 50, fontSize: "24px" }}
        >
          {totalCounters}
        </span>
        Items
      </div>
    </nav>
  );
};

--------

// 第二种。。
class NavBar extends Component {
  render() {
    return (
      <nav className="navbar navbar-light">
        <div className="navbar-brand">
          <i className="fa fa-shopping-cart fa-lg m-2" aria-hidden="true" />
          <span
            className="badge badge-pill badge-info m-2"
            style={{ width: 50, fontSize: "24px" }}
          >
            {this.props.totalCounters} // 使用props
          </span>
          Items
        </div>
      </nav>
    );
  }
}

export default NavBar;

总结

学习React的第一个案例

  • react的组件划分也要靠视图来判断
  • App.js中,定义好了方法,直接往下传递即可
  • 多个数据用map来渲染,在map中使用key
  • 改变state,要用setState
  • 每个react模块,都要引用react,import react!
  • {}里可以放数据,放方法,放任意Js表达式
  • 组件的后缀是 jsx
  • 继承component,一定要实现render方法。
  • React组件命名,使用大驼峰
  • 组件要用export导出

好的决心必须以行动来贯彻,没有行动,好的决心没有任何意义。