【React】01-React 入门到放弃系列

01-React 入门


背景

由于捣鼓一些项目需要用到React,找了一些文档入门实践了一番。本篇文章以一个学生成绩录入的表单为例子,记录React 入门的一些基础操作。

入门

该操作的前提是本地安装了NodeJS环境。根据官网给的例子,我们使用如下命令初始化项目

npx create-react-app react4j

目录结构如下:

cpp 复制代码
react-practice
├── react4j
│   ├── public
│   │   ├── favicon.ico
│   │   ├── index.html
│   │   ├── logo192.png
│   │   ├── logo512.png
│   │   ├── manifest.json
│   │   └── robots.txt
│   ├── src
│   │   ├── App.css
│   │   ├── App.js
│   │   ├── App.test.js
│   │   ├── index.css
│   │   ├── index.js
│   │   ├── logo.svg
│   │   ├── reportWebVitals.js
│   │   └── setupTests.js
│   ├── README.md
│   ├── package-lock.json
│   ├── package.json
└── list.md

● 目录 src :React 应用源码存放的目录。

● 目录 public 包含了开发应用时浏览器会读取的文件,其中最重要的就是 index.html。React 将目录 src 中的代码嵌入这个文件,从而浏览器才能运行此文件。

● 文件 package.json 包含了 Node.js/npm 为了建立该应用程序所管理着的文件信息

● manifest.json 文件定义了应用的名称、简称、图标、启动 URL、显示模式、主题颜色和背景色

启动项目,这里我们可以预览:

npm start

关于 index.html,是项目的主页面,这里只定义一个id为root的标签:

javascript 复制代码
<body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
  </body>
</html>

关于index.js,这里引入我们的App组件,App组件定义在App.js文件里。

javascript 复制代码
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  // <React.StrictMode>
  //   <App />
  // </React.StrictMode>
  <App subject="Clarice" />
);

关于App.js ,由三部分组成:顶部的 import 语句,中间的 App 组件,以及底部的 export 语句。通过export ,我们可以在项目入口的 index.js文件里用 <APP /> 标签直接使用该组件。

这里的函数命名 App 也是React 特殊的命名方式,它使用帕斯卡命名法,如"HelloWorld",来帮助用户辨认一个 JSX 元素是 React 组件而非普通的 HTML 标签。如果你将函数名"App"改为"app",你的浏览器会显示错误。

javascript 复制代码
import React from "react";
import logo from "./logo.svg";
import "./App.css";

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer">
          Learn React
        </a>
      </header>
    </div>
  );
}
export default App;

学生成绩录入的表单

  1. 删除多余的文件 App.test.js App.css logo.svg serviceWorker.js setupTests.js
  2. 在App.js 编写UI,这里存储数据先暂时用localStorage存储录入的数据。
cpp 复制代码
import React, { useState, useEffect } from 'react';


const ScoreTable = () => {
  console.log(111)
  const [scores, setScores] = useState([]);
  const [studentName, setStudentName] = useState('');
  const [studentScore, setStudentScore] = useState('');

  useEffect(() => {
    // 从localStorage中读取数据,并设置为初始state值
    const storedScores = localStorage.getItem('scores');
    if (storedScores) {
      setScores(JSON.parse(storedScores));
    }
  }, []);

  useEffect(() => {
    // 每次scores更新后,将数据保存到localStorage中
    localStorage.setItem('scores', JSON.stringify(scores));
  }, [scores]);
  
  const handleNameChange = (e) => {
    setStudentName(e.target.value);
  };

  const handleScoreChange = (e) => {
    setStudentScore(e.target.value);
  };

  const handleAddScore = () => {
    if (studentName && studentScore) {
      const newScore = {
        name: studentName,
        score: studentScore
      };
      setScores([...scores, newScore]);
      setStudentName('');
      setStudentScore('');
    }
  };


  return (
    <div className="todoapp stack-large">
      <h1>学生成绩录入</h1>
      <form>
        <label htmlFor="new-todo-input" className="label__lg">
          学生姓名:
        </label>
        <input
          type="text"
          id="new-todo-input"
          className="input input__lg"
          name="text"
          value={studentName}
          autoComplete="off"
          onChange={handleNameChange}
        />
        <label htmlFor="new-todo-input" className="label__lg">
          学生成绩:
        </label>

        <input
          type="text"
          id="new-todo-input"
          className="input input__lg"
          name="text"
          value={studentScore}
          autoComplete="off"
          onChange={handleScoreChange}
        />

        <button type="submit" className="btn btn__primary btn__lg" onClick={handleAddScore}>
          新增
        </button>
      </form>

      <h2 id="list-heading">名单列表</h2>
      <table>
        <thead>
          <tr>
            <th>姓名</th>
            <th>成绩</th>
          </tr>
        </thead>
        <tbody>
          {scores.map((score, index) => (
            <tr key={index}>
              <td>{score.name}</td>
              <td>{score.score}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}


function App(props) {
  console.log(props)

  return (
    <ScoreTable />
  );
}

export default App;
  1. 编写CSS样式,可以直接copy如下的CSS样式,取代原来的index.css文件:
typescript 复制代码
/* RESETS */
*,
*::before,
*::after {
  box-sizing: border-box;
}
*:focus {
  outline: 3px dashed #228bec;
  outline-offset: 0;
}
html {
  font: 62.5% / 1.15 sans-serif;
}
h1,
h2 {
  margin-bottom: 0;
}
ul {
  list-style: none;
  padding: 0;
}
button {
  border: none;
  margin: 0;
  padding: 0;
  width: auto;
  overflow: visible;
  background: transparent;
  color: inherit;
  font: inherit;
  line-height: normal;
  -webkit-font-smoothing: inherit;
  -moz-osx-font-smoothing: inherit;
  -webkit-appearance: none;
}
button::-moz-focus-inner {
  border: 0;
}
button,
input,
optgroup,
select,
textarea {
  font-family: inherit;
  font-size: 100%;
  line-height: 1.15;
  margin: 0;
}
button,
input {
  overflow: visible;
}
input[type="text"] {
  border-radius: 0;
}
body {
  width: 100%;
  max-width: 68rem;
  margin: 0 auto;
  font:
    1.6rem/1.25 Arial,
    sans-serif;
  background-color: #f5f5f5;
  color: #4d4d4d;
}
@media screen and (min-width: 620px) {
  body {
    font-size: 1.9rem;
    line-height: 1.31579;
  }
}
/*END RESETS*/
/* GLOBAL STYLES */
.form-group > input[type="text"] {
  display: inline-block;
  margin-top: 0.4rem;
}
.btn {
  padding: 2px;
  margin-left: 5px;
  border: 0.2rem solid #4d4d4d;
  cursor: pointer;
  text-transform: capitalize;
}
.btn.toggle-btn {
  border-width: 1px;
  border-color: #d3d3d3;
}
.btn.toggle-btn[aria-pressed="true"] {
  text-decoration: underline;
  border-color: #4d4d4d;
}
.btn__danger {
  color: #fff;
  background-color: #ca3c3c;
  border-color: #bd2130;
}
.btn__filter {
  border-color: lightgrey;
}
.btn__primary {
  color: #fff;
  background-color: #000;
}
.btn-group {
  display: flex;
  justify-content: space-between;
}
.btn-group > * {
  flex: 1 1 49%;
}
.btn-group > * + * {
  margin-left: 0.8rem;
}

.visually-hidden {
  position: absolute !important;
  height: 1px;
  width: 1px;
  overflow: hidden;
  clip: rect(1px 1px 1px 1px);
  clip: rect(1px, 1px, 1px, 1px);
  white-space: nowrap;
}
[class*="stack"] > * {
  margin-top: 0;
  margin-bottom: 0;
}
.stack-small > * + * {
  margin-top: 1.25rem;
}
.stack-large > * + * {
  margin-top: 2.5rem;
}
@media screen and (min-width: 550px) {
  .stack-small > * + * {
    margin-top: 1.4rem;
  }
  .stack-large > * + * {
    margin-top: 2.8rem;
  }
}
.stack-exception {
  margin-top: 1.2rem;
}
/* END GLOBAL STYLES */
.todoapp {
  background: #fff;
  margin: 2rem 0 4rem 0;
  padding: 1rem;
  position: relative;
  box-shadow:
    0 2px 4px 0 rgba(0, 0, 0, 0.2),
    0 2.5rem 5rem 0 rgba(0, 0, 0, 0.1);
}
@media screen and (min-width: 550px) {
  .todoapp {
    padding: 4rem;
  }
}
.todoapp > * {
  max-width: 50rem;
  margin-left: auto;
  margin-right: auto;
}
.todoapp > form {
  max-width: 100%;
}
.todoapp > h1 {
  display: block;
  max-width: 100%;
  text-align: center;
  margin: 0;
  margin-bottom: 1rem;
}
.label__lg {
  display: inline-block;
  line-height: 1.01567;
  font-weight: 300;
  padding: 0.8rem;
  margin-bottom: 1rem;
  text-align: center;
}
.input__lg {
  padding: 1px;
  border: 2px solid #000;
}
.input__lg:focus {
  border-color: #4d4d4d;
  box-shadow: inset 0 0 0 2px;
}
[class*="__lg"] {
  display: inline-block;

  font-size: 14px;
}
[class*="__lg"]:not(:last-child) {
  font-size: 14px;
}
@media screen and (min-width: 620px) {
  [class*="__lg"] {
    font-size: 14px;
  }
}
.filters {
  width: 100%;
  margin: unset auto;
}

table {
  width: 100%;
  border-collapse: collapse;
  font-family: Arial, sans-serif;
  color: #333;
}

table th, table td {
  padding: 8px;
  text-align: left;
  border-bottom: 1px solid #ddd;
}

table th {
  background-color: #f2f2f2;
  font-weight: bold;
}

table tr:nth-child(even) {
  background-color: #f9f9f9;
}

table tr:hover {
  background-color: #e6e6e6;
  cursor: pointer;
}

input[type="text"] {
  padding: 8px;
  border: 1px solid #ddd;
  border-radius: 4px;
}
  1. npm start 预览:

小结

  1. 本人实践下来,跟原来HTML + JavaScript 最明显的区别就是,React把UI封装到一个个JS函数里面去了。这跟它官网的描述也挺相符的, 通过使用组件最大程度地减少开发人员构建 UI 时发生的错误。。

  2. React 与 JavaScript 的最大区别在于 JSX 语法的使用上。JSX 是在 JavaScript 语法上的拓展,因此类似于 HTML 的代码可以和 JSX 共存。更多资料可以看看官方文档

相关推荐
我爱李星璇1 分钟前
HTML常用表格与标签
前端·html
疯狂的沙粒5 分钟前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript
小镇程序员21 分钟前
vue2 src_Todolist全局总线事件版本
前端·javascript·vue.js
野槐23 分钟前
前端图像处理(一)
前端
程序猿阿伟30 分钟前
《智能指针频繁创建销毁:程序性能的“隐形杀手”》
java·开发语言·前端
疯狂的沙粒32 分钟前
对 TypeScript 中函数如何更好的理解及使用?与 JavaScript 函数有哪些区别?
前端·javascript·typescript
瑞雨溪40 分钟前
AJAX的基本使用
前端·javascript·ajax
力透键背43 分钟前
display: none和visibility: hidden的区别
开发语言·前端·javascript
程楠楠&M1 小时前
node.js第三方Express 框架
前端·javascript·node.js·express
盛夏绽放1 小时前
Node.js 和 Socket.IO 实现实时通信
前端·后端·websocket·node.js