React小计

React小计

事件

// App.js
// rcc : 快捷代码块,  前提是安装了必备的 React 插件
import React, { Component } from "react";
// npm管理的项目,  之前的脚本 就化身为 模块

export default class App extends Component {
  // 事件
  show() {
    alert("点击事件!");
    return () => alert("我是返回值");
  }

  render() {
    return (
      <div>
        <h1>Hello World!</h1>
        {/* 
        点击事件:
        原生开发: οnclick=""
        vue开发: @click=""
        小程序: bind-tap=""
        */}
        {/* this.show:  this代表当前对象 */}
        <button onClick={this.show}>点击</button>
        {/* 注意事项: {}中的代码在页面显示时会自动运行 */}
        <div>{4 + 5}</div>
        {/* 开发阶段: render() 会渲染两次 */}
        {/* {this.show()} */}
      </div>
    );
  }
}

事件中的this指向

// 事件中的this指向
import React, { Component } from "react";

export default class App extends Component {
  name = "Lena";

  // 普通函数 在严格模式下, 事件触发时, 其中this指向 undefined
  show() {
    console.log("this:", this);
    console.log(this.name);
  }

  // 箭头函数: ES6新特性, 弥补了 普通函数this指向多变的缺点; 自带this指向保持
  jiantou = () => {
    console.log(this);
    console.log(this.name);
  };

  // 组件生成的方式: new App().render()
  render() {
    console.log("render中的this:", this);

    return (
      <div>
        {/* 事件由window触发, window触发的普通函数, 其中的this是undefined */}
        {/* 通过bind(): 为show函数指定 this 的执行为当前对象 */}
        <button onClick={this.show.bind(this)}>普通函数</button>
        <button onClick={this.jiantou}>箭头函数</button>
        {/* 解决方案: 利用箭头函数 调用 普通函数 */}
        {/* 原格式: ()=>{ return this.show(); } */}
        {/* 语法糖: ()=>{return xxx;}  则可以简化: ()=>xxx */}
        {/* 点击之后: 先执行  箭头函数,  箭头函数再执行内部的 this.show();  由于箭头函数自带 this指向保持,  所以 this.show() 中的this 就是当前App */}
        <button onClick={() => this.show()}>箭头+普通</button>
      </div>
    );
  }
}

事件传参

// 事件传参
import React, { Component } from "react";

export default class App extends Component {
  show(name) {
    console.log(name);
  }

  render() {
    return (
      <div>
        <h3>TA最喜欢:</h3>
        {/* 错误写法: {}中代码会自动执行, show方法的返回值是 空, 点击后执行的是空 */}
        <button onClick={this.show("胡了哥")}>胡了哥</button>
        {/* 正确: 利用bind 提前绑定好参数, 但是不会执行;  参数1 要求必须是this */}
        <button onClick={this.show.bind(this, "彭小鱼晏")}>彭小鱼晏</button>
        {/* 正确: 点击后执行箭头, 箭头再执行内部的 show 方法 */}
        <button onClick={() => this.show("吴燕祖")}>吴燕祖</button>
      </div>
    );
  }
}

状态值

// 状态值
// 微信小程序 借鉴了 React 的状态值写法:  data   setData()
// setData 在更新 data 中数据的同时, 会刷新界面

// React中: state 和 setState()

import React, { Component } from "react";

export default class App extends Component {
  state = { num: 1 };

  // 非官方推荐 但是使用较多: 单纯使用 setState() 的刷新效果
  count = 100;

  changeCount = () => {
    this.count++;
    // 单纯刷新界面:  利用 setState 刷新界面的特性.  参数必须传递
    this.setState({}); //快捷 sst
  };

  doAdd() {
    // this.state.num++;
    // console.log(this.state.num);
    // 必须使用 setState 方法才能刷新界面
    this.setState({ num: this.state.num + 1 });
  }

  render() {
    return (
      <div>
        <button onClick={this.doAdd.bind(this)}>{this.state.num}</button>
        {/* 箭头函数, 不需要bind */}
        <button onClick={this.changeCount}>{this.count}</button>
      </div>
    );
  }
}

setState() 的异步性

// setState() 的异步性
import React, { Component } from "react";

export default class App extends Component {
  state = { num: 1 };

  doAdd = () => {
    // 把 xxx 数据 更新到界面上 : 此操作较慢
    // 为了防止此慢速操作影响到下方代码执行, 所以被放在了其它 线程中
    // setState(要更新的值, 更新完毕的回调函数)
    this.setState({ num: this.state.num + 1 }, () => {
      console.log("num更新完毕, 值:", this.state.num);
    });

    // 立刻让打印 num 的值:  由于速度快, 界面还没更新完, 所以出现的是之前的值
    console.log(this.state.num);
  };

  render() {
    return (
      <div>
        <button onClick={this.doAdd}>{this.state.num}</button>
      </div>
    );
  }
}

双向数据绑定

// 双向数据绑定: vue提供了v-model语法糖, 快速实现双向绑定;  但是React未提供, 只能自己来实现双向绑定效果
import React, { Component } from "react";

export default class App extends Component {
  state = { word: "123456" };
  // 细节: 习惯上和 组件的事件绑定的方法, 以 _ 开头
  _wordChanged = (e) => {
    // 事件触发的函数, 默认接收事件对象作为 最后一个参数
    console.log(e.target.value);
    this.setState({ word: e.target.value });
  };

  render() {
    return (
      <div>
        {/* 
        方向1: 把值传递给页面
        方向2: 页面变化时(单选框,输入框,多选..), 反向修改绑定的数据
        */}
        <input
          type="text"
          value={this.state.word}
          onChange={this._wordChanged}
        />
        <br />
        <div>{this.state.word}</div>
        {/* 双向绑定的事件比较简单, 可以直接写在 JSX 里 */}
        <input
          type="text"
          value={this.state.word}
          onChange={(e) => this.setState({ word: e.target.value })}
        />
      </div>
    );
  }
}

动态样式

// 动态样式

import React, { Component } from "react";

export default class App extends Component {
  // 变化的值: 用 state 状态值
  state = { fs: 16 };

  _doAdd() {
    this.setState({ fs: this.state.fs + 10 });
  }

  render() {
    return (
      <div>
        <button onClick={() => this._doAdd()}>变大</button>
        {/* 单位默认 px, 可以省略 */}
        <div style={{ fontSize: this.state.fs }}>Hello World!</div>
      </div>
    );
  }
}

外部样式 css文件

// 外部样式 css文件
import React, { Component } from "react";

/**
 * 引入 App.css 文件
 *
 * html: <link ...
 * css: @import url(....)
 * js: import '...css';
 *
 * 注意: import 是用来引入模块的, 如果引入文件则必须带 路径前缀: ./ ../  /
 */
import "./App.css";

export default class App extends Component {
  render() {
    return (
      <div>
        <div className="btn">普通按钮</div>
        <div className="btn default">默认按钮</div>
        <div className="btn success">success</div>
      </div>
    );
  }
}

.btn {
  display: inline-block;
  border-radius: 4px;
  padding: 3px 10px;
  font-size: 18px;
}

.btn.default {
  background-color: #9cc;
  color: white;
}

.btn.success {
  background-color: green;
  color: white;
}

计数器

// 计数器(小练习)
import React, { Component } from "react";

export default class App extends Component {
  // 如果页面上某个数据 会变化: 考虑状态值state
  state = { count: 5 };

  // _ : 习惯上, 与事件绑定的方法, 添加 _ 前缀
  // 普通函数 & 事件触发 & 严格模式 : 函数中的this指向undefined
  _doAdd() {
    // 更新数据 && 更新界面 : setState()
    // 假设 count = 5;   {count: count++}  其值为 {count: 5}
    this.setState({ count: this.state.count + 1 });
  }

  // 箭头函数: 自带this保持, 永远不变
  _doMinuse = () => {
    this.setState({ count: this.state.count - 1 });
  };

  render() {
    return (
      <div>
        <div>
          <button onClick={this._doMinuse} disabled={this.state.count === 1}>
            -
          </button>
          <span>{this.state.count}</span>
          <button onClick={this._doAdd.bind(this)}>+</button>
        </div>
      </div>
    );
  }
}

购物车的cell

// 购物车的cell(小练习)
import React, { Component } from "react";

// 引入外部css: 必须加路径标识
import "./App.css";

export default class App extends Component {
  //变化值: 放state
  state = { price: 6666, count: 4, new_price: 6666 };

  render() {
    const { price, count } = this.state;

    return (
      <div>
        <div className="work2 cell">
          <span>iPhone</span>
          <span>¥{price}</span>
          <div>
            <button
              onClick={() => this.setState({ count: count - 1 })}
              disabled={count == 1}
            >
              -
            </button>
            <span>{count}</span>
            <button onClick={() => this.setState({ count: count + 1 })}>
              +
            </button>
          </div>
          <span>总价: ¥{price * count}</span>
        </div>

        <div>
          <input
            type="text"
            value={this.state.new_price}
            onChange={(e) => this.setState({ new_price: e.target.value })}
          />
          <button
            onClick={() => this.setState({ price: this.state.new_price })}
          >
            确定修改
          </button>
        </div>
      </div>
    );
  }
}

变圆变方

// 变圆变方(小练习)
import React, { Component } from "react";

export default class App extends Component {
  state = { radius: 0 };
  // 状态值: 是圆 还是 方, 此值不需要显示在页面上; 可以不放在state里
  status = 0; //设定: 0 变圆  1 变方

  _doChanged = () => {
    // setState(变更的值, 变更后回调方法)
    this.setState(
      { radius: this.state.radius + [10, -10][this.status] },
      () => {
        // 0 -> 50: 圆形, 该变成方形
        if (this.state.radius == 50) this.status = 1;
        // 50 -> 0: 方形, 该变成圆形
        if (this.state.radius == 0) this.status = 0;

        this.setState({}); //刷新页面
      }
    );
  };

  render() {
    return (
      <div>
        <button onClick={this._doChanged}>
          {/* [][] :此写法 一定是 数组[序号] */}
          {["变圆", "变方"][this.status]}
        </button>
        <div
          style={{
            width: 100,
            height: 100,
            backgroundColor: "red",
            borderRadius: this.state.radius,
          }}
        ></div>
      </div>
    );
  }
}

条件渲染

// 条件渲染
// 对应 v-if
import React, { Component } from "react";

export default class App extends Component {
  state = { item: 0 };

  // React 的 条件渲染: 依赖于原生 if 判断语法
  showDetail() {
    const item = this.state.item;

    if (item == 0) return <div>Lena Show</div>;
    if (item == 1) return <div>Nancy Show</div>;
    if (item == 2) return <div>Miya Show</div>;
  }

  render() {
    return (
      <div>
        <div>
          <button onClick={() => this.setState({ item: 0 })}>Lena</button>
          <button onClick={() => this.setState({ item: 1 })}>Nancy</button>
          <button onClick={() => this.setState({ item: 2 })}>Miya</button>
        </div>
        {/* 事件触发的函数, {} 中不要写()  会直接调用 */}
        {/* 此处非事件, 就是希望页面显示时直接触发, 所以必须加() */}
        {/* 非事件触发的函数, 不需要考虑 this 指向 */}
        <div>{this.showDetail()}</div>
      </div>
    );
  }
}

列表渲染

// 列表渲染: v-for
import React, { Component } from "react";

export default class App extends Component {
  skills = ["html", "css", "js", "express", "vue"];

  emps = [
    { name: "Lena", age: 19, gender: 0 },
    { name: "Nancy", age: 20, gender: 0 },
    { name: "MiYa", age: 18, gender: 0 },
    { name: "Lucy", age: 21, gender: 0 },
  ];

  // 把数组中每个元素放在按钮中
  showKills() {
    let arr = [];

    // 遍历数组中每个元素, 放到button中, 然后保存到新的数组里
    this.skills.forEach((item, index) => {
      // JSX代码放数组里, 要求每一项都有 唯一标识 key
      const a = <button key={index}>{item}</button>;
      arr.push(a);
    });

    // arr中 都是按钮
    return arr;
  }

  showEmps() {
    let arr = [];

    this.emps.forEach((item, index) => {
      arr.push(
        <tr key={index}>
          <td>{index + 1}</td>
          <td>{item.name}</td>
          <td>{item.age}</td>
          <td>{["女", "男"][item.gender]}</td>
        </tr>
      );
    });

    return arr;
  }

  render() {
    return (
      <div>
        {/* 数组内容会自动挨个显示出来 */}
        <div>{this.skills}</div>
        <div>{this.showKills()}</div>
        <table border="1">
          <thead>
            <tr>
              <td>序号</td>
              <td>姓名</td>
              <td>年龄</td>
              <td>性别</td>
            </tr>
          </thead>
          <tbody>{this.showEmps()}</tbody>
        </table>
      </div>
    );
  }
}

列表渲染 map方法实现

// map方法实现 列表渲染
import React, { Component } from "react";

export default class App extends Component {
  skills = ["vue", "react", "angular", "echars", "dom"];

  showSkills = () =>
    this.skills.map((item, index) => <button key={index}>{item}</button>);

  // 箭头函数语法糖:  ()=>{return xxx;}  简化为: ()=>xxx
  showSkills_3() {
    return this.skills.map((item, index) => (
      <button key={index}>{item}</button>
    ));
  }

  showSkills_2() {
    return this.skills.map((item, index) => {
      return <button key={index}>{item}</button>;
    });
  }

  showSkills_1() {
    const arr = this.skills.map((item, index) => {
      return <button key={index}>{item}</button>;
    });

    return arr;
  }

  render() {
    return (
      <div>
        <div>{this.showSkills()}</div>
      </div>
    );
  }
}

生命周期

// 生命周期
import React, { Component } from "react";

export default class App extends Component {
  state = { show: false, name: "Lena" };

  render() {
    return (
      <div>
        <button onClick={() => this.setState({ show: !this.state.show })}>
          显示/隐藏
        </button>
        <button onClick={() => this.setState({ name: this.state.name + "Lena" })}>
          变更name
        </button>
        {this.state.show ? <Son name={this.state.name} /> : null}
      </div>
    );
  }
}

class Son extends Component {
  // 钩子函数: 生命周期在不同解决触发 固定的方法
  componentDidMount() {
    //挂载完毕
    console.log("componentDidMount: 组件挂载完毕");
  }

  // 通过返回值, 决定页面要不要刷新
  // 参数为想要更新的结果
  // 实际应用中: 可以利用一些 diff 算法 避免一些不必要的刷新.
  shouldComponentUpdate(props, state) {
    console.log("shouldComponentUpdate: 是否要更新页面?");
    console.log("更新后props:", props);
    console.log("更新后state:", state);
    // 希望不显示偶数
    if (state.num % 2 == 0) return false; //false不刷新

    return true; //true 刷新 ,  执行 render() 方法
  }

  // 参数时 更新前的值
  componentDidUpdate(props, state) {
    // props 是外来属性传参
    console.log("更新前props:", props);
    console.log("更新后props:", this.props);
    console.log("更新前state:", state);
    console.log("更新后state:", this.state);
  }

  componentWillUnmount() {
    //将要卸载
    console.log("componentWillUnmount: 组件将要卸载");
  }

  state = { num: 1 };

  render() {
    // let num = this.state.num;

    return (
      <div>
        <h4>Son组件</h4>
        <button onClick={() => this.setState({ num: this.state.num + 1 })}>
          {this.state.num}
        </button>
        <div>{this.props.name}</div>
      </div>
    );
  }
}

网络请求

// 网络请求
import React, { Component } from "react";

import "./App.css";

export default class App extends Component {
  // 会变化 && 放在页面上的  都放state
  state = { result: [] };

  componentDidMount() {
    const url = "https://api.apiopen.top/getWangYiNews";

    // axios:  this.axios.get(url).then(res=>{})
    // react
    fetch(url)
      .then((res) => res.json())
      .then((res) => {
        console.log(res);

        this.setState({ result: res.result });
      });
  }

  showNews = () =>
    this.state.result.map((item, index) => (
      <div key={index} className="news cell">
        <img src={item.image} alt="" />
        <div>
          <div>{item.title}</div>
          <div>{item.passtime}</div>
        </div>
      </div>
    ));

  render() {
    return <div>{this.showNews()}</div>;
  }
}

网络请求(跨域)

// 网络请求(跨域)
import React, { Component } from "react";

import "./App.css";

export default class App extends Component {
  // 关于请求返回值的默认值:  数组=[]  对象=null
  state = { data: null };

  componentDidMount() {
    // const url = "https://m.douyu.com/api/room/list?page=1&type=yz";
    const url = "/douyu/api/room/list?page=1&type=yz";

    fetch(url)
      .then((res) => res.json())
      .then((res) => {
        console.log(res);

        this.setState({ data: res.data });
      });
  }

  showRooms = () =>
    this.state.data.list.map((item, index) => (
      <div key={index} className="douyu cell">
        <div>
          <img src={item.roomSrc} alt="" />
          <div>{item.hn}</div>
          <div>{item.nickname}</div>
        </div>
        <div>{item.roomName}</div>
      </div>
    ));

  render() {
    // 如果页面数据是网络请求获取的, 应该判断是否存在
    if (!this.state.data) return <div></div>;

    return (
      <div
        style={{
          // width: 630,
          display: "flex",
          flexWrap: "wrap",
        }}
      >
        {this.showRooms()}
      </div>
    );
  }
}

// setupProxy.js
// 接上
// 创建配置文件, 在 src 目录下: setupProxy.js
// src/setupProxy.js
const { createProxyMiddleware } = require("http-proxy-middleware");

module.exports = function (app) {
  app.use(
    createProxyMiddleware("/douyu", {
      target: "https://m.douyu.com", //转发到的域名
      changeOrigin: true, // 域名有变化
      secure: true, // https 请求
      pathRewrite: {
        "^/douyu": "", // 接口地址中开头的 /douyu 重置为空
      },
    })
  );
  // 如果有更多的跨域, 就在下方继续写 app.user()...
};

路由系统

import React, { Component } from "react";

// 引入路由相关的所有 模块
import {
  Link, //相当于 router-link, 制作跳转超链接
  Switch, //相当于 router-view, 负责根据路径切换组件
  BrowserRouter as Router, // 相当于 router, 管理整个路由系统
  Route, // 管理 路径 和 组件的对应关系
} from "react-router-dom";

// 引入其它的组件
import About from "./pages/About";
import Home from "./pages/Home";
import News from "./pages/News";
import Contact from "./pages/Contact";

export default class App extends Component {
  render() {
    // 最外层标签写 Router, 这样内容就被路由管理
    return (
      <Router>
        <div style={{ border: "1px solid purple" }}>
          <Link to="/home">Home</Link>
          <br />
          <Link to="/news/lena/1">News:lena</Link>
          <br />
          <Link to="/news/nancy/2">News:nancy</Link>
          <br />
          <Link to="/about">About</Link>
          <br />
          <Link to="/contact">Contact</Link>
        </div>
        <div>
          {/* Switch: 类比 router-view,  用于切换组件 */}
          <Switch>
            {/* 路径的模糊匹配: 默认是模糊匹配, 此处就是 / 开头都匹配成功 */}
            {/* exact: 精确匹配 */}
            <Route path="/" exact component={Home} />

            <Route path="/home" component={Home} />
            {/* 利用 : 来代表参数, 同 vue */}
            <Route path="/news/:title/:id" component={News} />
            <Route path="/about" component={About} />
            <Route path="/contact" component={Contact} />
          </Switch>
        </div>
      </Router>
    );
  }
}

路由参数

// src/pages/News
import React, { Component } from "react";
import { withRouter } from "react-router-dom";

class News extends Component {
  render() {
    console.log(this.props);
    const { id, title } = this.props.match.params;

    /**
     * vue中:
     * this.$route : 存储路由相关值
     * this.$router: 存储操作路由的相关函数
     */

    return (
      <div>
        <h2>News页面</h2>
        <div>
          {id}, {title}
        </div>
        <button onClick={() => this.props.history.push("/home")}>
          前往Home页面
        </button>
      </div>
    );
  }
}

// withRouter: 向组件 News 的属性 props 中注入路由相关代码
export default withRouter(News);

Redux

// src/index.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";

// Provider: 专门用于 集成store到React
import { Provider } from "react-redux";
// Redux管理的store
import store from "./store";

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById("root")
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

/**
 * Redux: 符合 Flux 思想的一种具体实现的框架
 * - 多组件之间共享的数据 要统一管理
 * - 统一管理的数据 只暴露一种修改方式
 * - 可以监听数据变化
 */
import { createStore } from "redux";

//1. 创建state, 存储管理的变量
const initState = { count: 1 };

//2. reducer: 就是VueX的mutations, 用来存储修改 state 值的相关方法
const reducer = (state = initState, action) => {
  switch (action.type) {
    case "increment":
      // 固定写法: {原值, 新值}
      return { ...state, count: state.count + 1 };
    case "minuse":
      return { ...state, count: state.count - 1 };
    default:
      return state;
  }
};

// 3. reducer 函数, 用 store 进行管理
const store = createStore(reducer);

// 4. 读取 state 的方法
// VueX: this.$store.state.count
// React
console.log(store.getState()); //读取的 reducer 的 default 返回值

// 5. 监听属性变更:
store.subscribe(() => {
  console.log("state有变化:", store.getState());
});

// 6. 调用修改属性的方法
// VueX: this.$store.commit('increment')
// React
store.dispatch({ type: "increment" });
// -1
store.dispatch({ type: "minuse" });

// 带参数
import { createStore } from "redux";

const initState = {
  num: 100,
  skills: ["vue", "react"],
  age: 19,
};

const reducer = (state = initState, action) => {
  switch (action.type) {
    case "add":
      return { ...state, num: state.num + 1 };
    case "jian":
      return { ...state, num: state.num - 1 };
    case "skills_add":
      const skill = action.skill;
      return { ...state, skills: [...state.skills, skill] };
    case "age_update":
      return { ...state, age: action.new_age };
    default:
      return state;
  }
};

const store = createStore(reducer);

store.subscribe(() => {
  console.log("变更:", store.getState());
});

store.dispatch({ type: "add" });
store.dispatch({ type: "jian" });

// 向 skills 中添加新的项目
store.dispatch({ type: "skills_add", skill: "Angular" });
store.dispatch({ type: "skills_add", skill: "DOM" });
store.dispatch({ type: "skills_add", skill: "Bootstrap" });

// 更新年龄
store.dispatch({ type: "age_update", new_age: 29 });

Hook

// rfc
// 在函数中 利用 Hook 特性, 实现状态值 和 生命周期
import React, { useState, useEffect } from "react";

export default function App() {
  // 相当于:   state={num:1}
  const [num, setNum] = useState(1);
  // setNum 只能用于更新 num 变量, 属于配对使用
  const [count, setCount] = useState(500);

  const [show, setShow] = useState(false);

  return (
    <div>
      <button onClick={() => setNum(num + 1)}>{num}</button>
      <br />
      <button onClick={() => setCount(count + 2)}>count:{count}</button>
      <hr />
      <button onClick={() => setShow(!show)}>显示/隐藏</button>
      {show ? <Son /> : null}
    </div>
  );
}

//子
function Son() {
  const [num, setNum] = useState(1);
  const [count, setCount] = useState(1);

  //Hook: 生命周期
  useEffect(() => {
    // 此处监听 挂载完毕 和 更新完毕
    console.log("组件挂载完毕 或 更新完毕");
    return () => {
      // 此处监听 卸载时
      console.log("组件将要卸载");
    };
  }, [num]); //参数2数组: 哪些属性变化时 能够触发生命周期

  return (
    <div style={{ border: "2px solid red" }}>
      <h2>子元素</h2>
      {/* 页面有变化时, 会触发 卸载 -> 更新 */}
      <button onClick={() => setNum(num + 1)}>num:{num}</button>
      <button onClick={() => setCount(count + 1)}>count:{count}</button>
    </div>
  );
}












评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值