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笔记)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。