文章目录- 1. 虚拟 DOM
- 2. JSX
- 3. ReactComponent
- 4. props 是参数
- 5. PropTypes
- 6. getDefaultProps() 获取默认 props
- 7. 上下文
- 8. 多个上下文
- 9. state
- 在setState中使用函数,而不是对象
- 10. 无状态组件
- 11. 使用 props.children 与子组件对话
- 在setState中使用函数,而不是对象
learn from 《React全家桶:前端开发与实例详解》 https://zh-hans.reactjs.org/tutorial/tutorial.html https://zh-hans.reactjs.org/docs/create-a-new-react-app.html#create-react-app
1. 虚拟 DOM
我们操作虚拟DOM,让 React 负责更改浏览器的 DOM
虚拟 DOM,指的是,表示实际 DOM 的 JavaScript 对象树
开发人员只需要返回需要的 DOM,React 负责转换,且性能有优化,速度很快(高效的差异算法、更新子树、批量更新DOM)
ReactElement
是 虚拟 DOM 中对 DOM 元素的表示
先创建 RE,再 render (RE, 到实际的DOM挂载位置, 回调函数)
2. JSX
JSX 是 JavaScript Syntax Extension
JSX可以很方便的编写 ReactElement(无状态,不可变)
层次结构
babel 插件 可以 将 JSX 转译成 JS
JSX 通常用 ()
包含起来,JSX属性 用 {}
包含,
JSX 内部注释 {/* 注释 */}
JSX 使用 className
标识类
JSX 不能使用 for
属性,而是 htmlFor
3. ReactComponent
ReactComponent
是一个 JS 对象,至少有一个 render()
函数,返回一个 ReactElement
4. props 是参数
props 是组件的输入
props 可以传递任何 JS 对象
- 基本类型、简单 JS 对象
- 原子操作、函数、React元素、虚拟DOM节点
5. PropTypes
是验证 props 传递的值 的一种方法,属性名 : PropsTypes (string, number, boolean, function, object, array, arrayOf, node, element)
代码语言:javascript复制import PropTypes from 'prop-types';
import React from 'react';
class DocumentedContainer extends React.Component {
static propTypes = {
children: PropTypes.oneOf([PropTypes.element, PropTypes.array])
};
render() {
return <div className="container">{this.props.children}</div>;
}
}
export default DocumentedContainer;
6. getDefaultProps() 获取默认 props
代码语言:javascript复制class Counter extends React.Component {
static defaultProps = {
initialValue: 1
}
...
}
使用 <Counter />
,<Counter initialValue={1}/>
两种用法效果一致
7. 上下文
从 React 16.3.0 开始,可以指定通过组件树向下传递的变量,无需手动将变量从父组件传递到子组件
React.createContext
只接受一个参数,上下文提供的默认值
相当于 全局公开
的属性
例如网站主题: theme.js
代码语言:javascript复制import React from 'react';
export const themes = {
light: {
foreground: '#222222',
background: '#e9e9e9'
},
dark: {
foreground: '#fff',
background: '#222222'
}
};
export const ThemeContext = React.createContext(themes.dark);
在 app 中: ThemeContext.Provider
用于把数据传递给子组件
import React, {Component} from 'react';
import {ThemeContext, themes} from './theme';
import './App.css';
import Header from './Header';
class App extends Component {
state = {theme: themes.dark};
changeTheme = evt => {
this.setState(state => ({
theme: state.theme === themes.dark ? themes.light : themes.dark
}));
};
render() {
return (
<div className="App">
<ThemeContext.Provider value={this.state.theme}>
<Header />
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
<button onClick={this.changeTheme}>Change theme</button>
</ThemeContext.Provider>
</div>
);
}
}
export default App;
Header.js ThemeContext.Consumer
从 Provider 父组件中获取主题
import React from 'react';
import logo from './logo.svg';
import {ThemeContext} from './theme';
export const Header = props => (
<ThemeContext.Consumer>
{theme => (
<header
className="App-header"
style={{backgroundColor: theme.background}}
>
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title" style={{color: theme.foreground}}>
Welcome to React
</h1>
</header>
)}
</ThemeContext.Consumer>
);
export default Header;
8. 多个上下文
user.js
代码语言:javascript复制import React from 'react';
export const UserContext = React.createContext(null);
Body.js
代码语言:javascript复制import React from 'react';
import {ThemeContext} from './theme';
import {UserContext} from './user';
export const Body = props => (
<ThemeContext.Consumer>
{theme => (
<header
className="App-header"
style={{backgroundColor: theme.background}}
>
<UserContext.Consumer>
<h1>{user => (user ? 'Welcome back' : 'Welcome')}</h1>
</UserContext.Consumer>
</header>
)}
</ThemeContext.Consumer>
);
export default Body;
9. state
Switch.css
代码语言:javascript复制.active {
background-color: #d5fffb;
font-weight: bold;
}
支付选项:
代码语言:javascript复制import React from "react";
import "../Switch.css"; // 导入样式
const CREDITCARD = "Creditcard";
const BTC = "Bitcoin";
class Switch extends React.Component {
state = {
payMethod: BTC // 默认支付选项
};
select = choice => {
return evt => {
this.setState({
payMethod: choice
}); // 返回一个函数
};
};
renderChoice = choice => {
// create a set of cssClasses to apply
const cssClasses = ["choice"]; // 样式选项
if (this.state.payMethod === choice) {
cssClasses.push("active"); // add .active class
} // 支付的选项样式,增加一个 active 样式
return (
<div className={cssClasses.join(" ")} onClick={this.select(choice)}>
{choice}
</div>
); // 把样式渲染给 choice
};
render() {
return (
<div className="switch">
{this.renderChoice(CREDITCARD)}
{this.renderChoice(BTC)}
Pay with: {this.state.payMethod}
</div>
);
}
}
export default Switch;
在setState中使用函数,而不是对象
为什么?下面是一个点击减少的按钮
使用对象的方式赋值给 state,如果用户点击过快,计算机非常慢,而 setState
是异步的,如果碰到更高优先级的响应过载,这个减少按钮的点击响应还在队列中等待,那么用户可能点了3次,但是最后数值只减少了1
- 状态转换依赖于当前状态时,最好使用函数来设置状态,避免这种Bug
decrement = () => {
// Appears correct, but there is a better way
const nextValue = this.state.value - 1;
this.setState({
value: nextValue
});
};
更好的写法如下:
代码语言:javascript复制 decrement = () => {
this.setState(prevState => {
return {
value: prevState.value - 1
};
});
};
- 通常在组件里存的状态越少越好,最好是从外部获取,状态多了,会使得系统的状态是什么样子的变得难以推理
- 可以使用多个无状态组件构成 一个有状态组件
10. 无状态组件
- React 中 只需要
render()
方法的组件
无状态组件,它不是一个类,我们不会引用 this 这种函数式组件,性能更好
代码语言:javascript复制const Header = function(props) {
return (<h1>{props.headerText}</h1>)
}
有状态
代码语言:javascript复制export class Header extends React.Component {
render() {
return (
<h1>{this.props.headerText}</h1>
);
}
}
11. 使用 props.children 与子组件对话
可以使用 this.props.children
引用树中的子组件
import PropTypes from 'prop-types';
import React from 'react';
class DocumentedContainer extends React.Component {
static propTypes = {
children: PropTypes.oneOf([PropTypes.element, PropTypes.array])
};
render() {
return <div className="container">{this.props.children}</div>;
}
}
export default DocumentedContainer;
处理子组件 :
map()
,返回调用函数的结果的数组forEach()
不收集结果
import PropTypes from 'prop-types';
import React from 'react';
class MultiChildContainer extends React.Component {
static propTypes = {
component: PropTypes.element.isRequired,
children: PropTypes.element.isRequired
};
renderChild = (childData, index) => {
return React.createElement(
this.props.component,
{}, // <~ child props
childData // <~ child's children
);
};
render() {
return (
<div className="container">
{React.Children.map(this.props.children, this.renderChild)}
</div>
);
}
}
export default MultiChildContainer;
React.Children.toArray()
函数 转成子元素的数组
import PropTypes from 'prop-types';
import React from 'react';
class ArrayContainer extends React.Component {
static propTypes = {
component: PropTypes.element.isRequired,
children: PropTypes.element.isRequired
};
render() {
const arr = React.Children.toArray(this.props.children);
return <div className="container">{arr.sort((a, b) => a.id < b.id)}</div>;
}
}
export default ArrayContainer;