React基础

# Hello

install

安装过程可以 Ctrl + C 停止,用 cnpm 或 npm 来替代脚手架的 yarn 安装

# 安装脚手架
yarn global add create-react-app

# 创建react项目
npx create-react-app myapp

hello

import React from 'react';

function App() {
  return (
    <h1>Hello world!</h1>
  );
}

export default App;

释放配置

默认配置被隐藏,如果希望显示在项目中(除非对webpack非常熟悉,否则不建议)

npm run eject

其他辅助脚本

# 构建项目
npm run build
yarn build

# 运行测试
npm run test
yarn test

# 另外一种构建方式
# required npm 6.0+
npm init react-app my-app
yarn create react-app my-app

# JSX

介绍

  • JSX语法是为了简化React的语法
  • JSX防注入攻,在渲染所有输入内容之前默认会转义(有效防XSS跨站脚本攻击)

注意事项

1、只能有一个根标签

2、单标签(img/input)必须闭合

3、不能使用for,class等关键字

使用方式

使用变量

const name = `React`;

function App() {
  return (
    <h1>Hello {name}!</h1>
  );
}

使用函数

const Hello = () => {
  return `Hello React!`;
}

function App() {
  return (
    <h1>{Hello()}</h1>
  );
}

使用map

<ul>
  {['React', 'Vue'].map(item => <li>{item}</li>)}
</ul>

直接数组会跟字符串相同

<h1>{['React', 'Vue']}</h1>  // 输出 ReactVue

# 组件

基本用法

组件名必须大写以区分函数和方法

函数组件(又名无状态组件:Hook出现以后取消无状态组件的叫法)

const Hello = () => <h1>Hello React!</h1>;

function App() {
  return (
    <Hello></Hello>
  );
}

有状态组件(Hook出现后几乎不用)

class Hello extends React.Component {
  render() {
    return <h1>Hello React!</h1>;
  }
}

function App() {
  return (
    <Hello></Hello>
  );
}

如果没有插槽(Vue叫法,React叫组合),建议写成单标签

写成多标签等于告诉其他开发者,这个组件可以使用插槽用法

const Hello = () => <h1>Hello React!</h1>;

function App() {
  return (
    <Hello />
  );
}

组件传参

除字符串外,必须用大括号包裹

const info = "React";

function App() {
  const Hello = (props) => <h1>Hello {props.info}!</h1>;
  return (
    <Hello info={info} />
  );
}

数据流动的概念:

  • 组件可以把自己或父组件的状态传给子组件
  • 这通常被称为自顶向下或单向数据流

参数只读

函数不更改传入的参数被称为纯函数

function sum(a, b) {
  return a + b;
}

函数能改变自身参数的,被称为非纯函数

function withdraw(account, amount) {
  account.total -= amount;
}

React必须像纯函数一样,不能更改参数的值

也就是说,子组件无法更新父组件的状态,只能让父组件自更新

# 生命周期

本章不重要

Hook出现之前,只有有状态组件才有生命周期

包含

实例化阶段

存在期阶段

销毁期阶段

实例化阶段

defaultProps:也就是初始化constructor的props参数

class App extends React.Component {
  static defaultProps = {
    name: "App"
  }
}

constructor:主要初始化状态

class App extends React.Component {
  constructor(props) {
    super(props);
  }
}

componentWillMount:最后一次修改状态的机会(很少用)

class App extends React.Component {
  componentWillMount() {

  }
}

render:渲染组件

class App extends React.Component {
  render() {
    return <h1>Hello world!</h1>
  }
}

componentDidMount:只执行一次,可获取DOM。ajax请求也一般放在这里

class App extends React.Component {
  componentDidMount() {
    
  }
}
graph LR
defaultProps-->Constructor 
Constructor-->componentWillMount 
componentWillMount-->render 
render-->componentDidMount 

存在期阶段

componentWillReceiveProps:组件发生时改变

shouldComponentUpdate:控制组件重新渲染

componentWillUpdate:组件更新前

render:渲染组件

componentDidUpdate:组件渲染结束

销毁期

组件销毁时执行的生命周期,用于停止定时器,DOM解绑,取消订阅等

class App extends React.Component {
  componentWillUnmount() {
    
  }
}

# state

本章非重要

Hook出现之前,只有有状态组件才状态管理

使用

使用this.state使用状态

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      date: new Date().toLocaleTimeString()
    }
  }

  render() {
    return <div>Hello React!{this.state.date}</div>
  }
}

变量接收

render() {
  const date = this.state.date;
  return <div>Hello React!{date}</div>
}

不能直接更新state

// 错误示范
this.state.comment = 'Hello';

更新状态

使用setState更新状态

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      date: new Date().toLocaleTimeString()
    }
  }

  componentDidMount() {
    setInterval(() => {
      this.setState({
        date: new Date().toLocaleTimeString()
      });
    }, 1000);
  }

  render() {
    const date = this.state.date;
    return <div>Hello React!{date}</div>
  }
}

setState 不要直接使用state,否则不更新视图

setState 会合并原有的状态

// state = {
//   name: "张三",
//   age: 12
// }

// 不会把age属性丢失
this.setState({
  name: "李四"
});

# 事件

事件

事件this默认指向window

解决方法一:constructor中绑定

constructor(props) {
  super(props);
  this.state = {
    count: 1
  }
  this.add = this.add.bind(this);
}

解决方法二:回调函数(有性能问题)

<button onClick={() => this.add.bind(this)}>+ 1</button>

解决方案三:最新的ES语法,暂时在stage3(脚手架支持)

add = () => {
  console.log(this);
}

事件对象

回调函数方式

add = (e, id) => {
  console.log(e);
}

render() {
  return <button onClick={(e) => this.add(e, `6`)}>+1</button>
}

bind方式

add = (id, e) => {
  console.log(e);
}

render() {
  return <button onClick={this.add.bind(this, `6`)}>+1</button>
}

阻止默认事件

button不写type默认是submit

React的阻止默认事件不能用return false

add = (id, e) => {
  e.preventDefault();
}

# 条件渲染

if

渲染DOM

render() {
  const login = true;
  if (login) return <div>欢迎您</div>
  else return <div>请登录</div>
}

渲染组件

render() {
  const login = true;
  if (login) return <Hello />
  else return <Role />
}

抽取变量

render() {
  const login = true;
  const View = login ? <Hello /> : <Role />;
  return <div>{View}</div>
}

运算符

render() {
  const login = true;
  return <div>{login && <Hello />}</div>
}

三目运算符

render() {
  const login = true;

  return <div>{login ? <Hello /> : <Role />}</div>
}

阻止组件渲染

组件返回null不会被渲染

const Hello = ({ show }) => {
  return show ? <div>欢迎您</div> : null
}

# 遍历

示例

function App() {
  let name = [1, 2, 3];
  return <div>{name.map(item => {
    return <p>{item * 2}</p>
  })}</div>
}

可以提取变量,增强阅读性

function App() {
  let name = [1, 2, 3];
  const nameItem = name.map(item => {
    return <p>{item * 2}</p>
  })
  return <div>{nameItem}</div>
}

keys

帮助识别DOM中某些元素的变化,提高diff算法的效率

一个元素的Key这个元素的列表中独一无二

const todoItems = todos.map((todo) =>
  <li key={todo.id}>
    {todo.text}
  </li>
);

元素没有id可以用索引值代替

const todoItems = todos.map((todo, index) =>
  <li key={index}>
    {todo.text}
  </li>
);

key必须放在最外层的组件或DOM中

const todoItems = todos.map((todo) =>
  <ListItem key={todo.id}></ListItem>
);

key在兄弟中唯一在全局不唯一

React遍历的key值无法通过组件传递

# 状态提升

非重要

Hook之后,都是用的函数组件

原理

多个子组件公用一套状态时,把这个状态提升到子组件公用的最近的父组件中

const ButtonMinus = (props) => {
  return (
    <>
      <h2>当前值:{props.coumt}</h2>
      <button onClick={props.change}>-1</button>
    </>
  )
}
const ButtonAdd = (props) => {
  return (
    <>
      <h2>当前值:{props.coumt}</h2>
      <button onClick={props.change}>+1</button>
    </>
  )
}

以上两个组件公用Change方法和count属性,提取到父组件中

# 组合继承

继承

React不推荐使用继承的方式来实现代码的重用

也很难实现,因为有状态组件都继承了React.Component,不能继承其他类

无状态组件继承其他类,那么其他类必须也是函数类型的,所以实现起来非常麻烦

组合

props.children访问组件中间的内容

const Button = (props) => {
  return props.children;
}

function App() {
  return <Button>确认</Button>
}

对未知容器的渲染非常友好

const Dialog = (props) => {
  return <div className="dialog">{props.children}</div>;
}

function App() {
  return (
    <Dialog>
      <p className="title">提示</p>
      <p className="content">密码错误!</p>
    </Dialog>
  )
}

组件不经可以传递值,还能传递组件

<Dialog
  left={<Hello />}
  right={<Kugou />}
/>

特例关系

即对已有的的组件封装特殊的实例

const Dialog = (props) => {
  return (
    <div className="dialog">
      <p className="title">{props.title}</p>
      <p className="content">{props.content}</p>
    </div>
  )
}

const Error404Dialog = () => <Dialog title="错误提示" content="404" />

# 导入拓展

拓展

React的webpack配置默认不支持SCSS和LESS,如果想要使用,需要自行安装

React有个很好的机制,一引即用,很多插件无须配置

yarn add node-sass
yarn add bootstrap

React有一个动画库非常有名 react-flip-move 官网

flip是一种做动画的思维,react-flip-move就是以此命名

yarn add react-flip-move

官方快速入手DEMO

/**
 * BEFORE:
 */
const TopArticles = ({ articles }) => (
  {articles.map(article => (
    <Article key={article.id} {...article} />
  ))}
);

/**
 * AFTER:
 */
import FlipMove from 'react-flip-move';

const TopArticles = ({ articles }) => (
  <FlipMove>
    {articles.map(article => (
      <Article key={article.id} {...article} />
    ))}
  </FlipMove>
);

插件推荐

谷歌浏览器拓展:React Developer Tools

安装以后点击Component可以清楚的看到React的状态及props

# 表单

受控组件

React的输入框,下拉框,单选多选框绑定状态以后都变得无效,只能通过setState来更新

constructor(props) {
  super(props);

  this.state = {
    name: ""
  }
}

handleChange = (e) => {
  this.setState({ name: e.target.value });

}

render() {
  return <input type="text" value={this.state.name} onChange={this.handleChange.bind(this)} />;
}

如果想通过一个事件控制多个表单,前提是表单的name属性与state属性相同

handleChange = (e) => {
  let state = {};
  state[e.target.name] = e.target.value;
  this.setState(state);
  // ES6 新语法
  // this.setState({[e.target.name],e.target.value});
}

select标签

render() {
  return (
    <select value={this.state.city} onChange={this.handleChange.bind(this)}>
      <option value="101010200">海淀</option>
      <option value="101010300">朝阳</option>
      <option value="101010400">顺义</option>
    </select>
  )
}

非受控组件

表单可以不绑定状态,就属于非受控组件。需要使用它时,使用ref获取DOM值(具体Hook笔记)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335