1、什么是jsx
JSX是JavaScript的扩展语法,这种<></>标签的写法就是 JSX。JSX 编写的组件通过预处理器 babel 解析后,再交给 React 库渲染到指定父容器下,形成最终html页面,供浏览器解析和显示。
JSX会被babel编译为:React.createElement(),React.createElement()将返回一个叫作“ReactElement”的JS对象。
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);
React DOM 会将元素和它的子元素与它们之前的状态进行比较,并只会进行必要的更新来使 DOM 达到预期的状态。
2、函数组件(无状态)和class组件(有状态state)
props不能再更改
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
3、react的生命周期
(1)当组件实例被创建并插入 DOM 中时
挂载:constructor() render() componentDidMount()
(2)当组件的 props 或 state 发生变化时会触发更新。
更新:render() componentDidUpdate(prevProps, prevState)
(3) 卸载 : componentWillUnmount()
4、state的更新是异步的,React 可能会把多个 setState()
调用合并成一个调用。
//使用之前的state作为参数时
// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
5、在class组件中正确使用this的三种方法
(1)正常定义的函数 需要在构造函数中绑定this
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// 为了在回调中使用 `this`,这个绑定是必不可少的
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
(2)正常定义的函数 在回调中使用箭头函数
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
// 此语法确保 `handleClick` 内的 `this` 已被绑定。
return (
<button onClick={() => this.handleClick()}>
Click me
</button>
);
}
}
(3)使用class fields语法定义函数
class LoggingButton extends React.Component {
// 此语法确保 `handleClick` 内的 `this` 已被绑定。
// 注意: 这是 *实验性* 语法。
handleClick = () => {
console.log('this is:', this);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
6、react的槽
(1)props.children (2)自定义props的名字
(1)
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
);
}
function WelcomeDialog() {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
Welcome
</h1>
<p className="Dialog-message">
Thank you for visiting our spacecraft!
</p>
</FancyBorder>
);
}
(2)自定义props的名字
function SplitPane(props) {
return (
<div className="SplitPane">
<div className="SplitPane-left">
{props.left}
</div>
<div className="SplitPane-right">
{props.right}
</div>
</div>
);
}
function App() {
return (
<SplitPane
left={
<Contacts />
}
right={
<Chat />
} />
);
}
7、refs:操作某个组件或者dom元素。你不能在函数组件上使用 ref
属性,因为他们没有实例。
(1)用React.createRef创建
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
render() {
return <div ref={this.myRef} />;
}
}
访问ref
const node = this.myRef.current;
(2)回调ref,ref是个回调函数
function CustomTextInput(props) {
return (
<div>
<input ref={props.inputRef} />
</div>
);
}
class Parent extends React.Component {
render() {
return (
<CustomTextInput
inputRef={el => this.inputElement = el}
/>
);
}
}
(3)refs转发:使用React.forwardRef包裹箭头函数
const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
));
// 你可以直接获取 DOM button 的 ref:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;
8、高阶组件HOC
高阶组件是参数为组件,返回值为新组件的函数。
HOC是一种复用组件逻辑的高级技巧。
HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。
使用举列:此高阶组件只是为了每次都输出传入FancyButton的props
./FancyButton内容如下:
class FancyButton extends React.Component {
focus() {
// ...
}
// ...
}
function logProps(WrappedComponent) {
class LogProps extends React.Component {
componentDidUpdate(prevProps) {
console.log('old props:', prevProps);
console.log('new props:', this.props);
}
render() {
return <WrappedComponent {...this.props} />;
}
}
return LogProps;
}
export default logProps(FancyButton);
============另一个文件开始使用此HOC===============================================
import FancyButton from './FancyButton';
const ref = React.createRef();
// 我们导入的 FancyButton 组件是高阶组件(HOC)LogProps。
// 尽管渲染结果将是一样的,
// 但我们的 ref 将指向 LogProps 而不是内部的 FancyButton 组件!
// 这意味着我们不能调用例如 ref.current.focus() 这样的方法
<FancyButton
label="Click Me"
handleClick={handleClick}
ref={ref}
/>;
(1)不要在render里创建hoc,这不仅仅是性能问题 - 重新挂载组件会导致该组件及其所有子组件的状态丢失。
React 的 diff 算法(称为协调)使用组件标识来确定它是应该更新现有子树还是将其丢弃并挂载新子树。 如果从 render
返回的组件与前一个渲染中的组件相同(===
),则 React 通过将子树与新子树进行区分来递归更新子树。 如果它们不相等,则完全卸载前一个子树。
render() {
// 每次调用 render 函数都会创建一个新的 EnhancedComponent
// EnhancedComponent1 !== EnhancedComponent2
const EnhancedComponent = enhance(MyComponent);
// 这将导致子树每次渲染都会进行卸载,和重新挂载的操作!
return <EnhancedComponent />;
}
(2)hoc的ref不会自动传递到被包裹组件上,该 ref 将引用最外层的容器组件logProps,需要使用React.forwardRef转发。
function logProps(Component) {
class LogProps extends React.Component {
componentDidUpdate(prevProps) {
console.log('old props:', prevProps);
console.log('new props:', this.props);
}
render() {
const {forwardedRef, ...rest} = this.props;
// 将自定义的 prop 属性 “forwardedRef” 定义为 ref
return <Component ref={forwardedRef} {...rest} />;
}
}
// 注意 React.forwardRef 回调的第二个参数 “ref”。
// 我们可以将其作为常规 prop 属性传递给 LogProps,例如 “forwardedRef”
// 然后它就可以被挂载到被 LogProps 包裹的子组件上。
return React.forwardRef((props, ref) => {
return <LogProps {...props} forwardedRef={ref} />;
});
}
注意:
1、组件名称必须以大写字母开头。
React 会将以小写字母开头的组件视为原生 DOM 标签。例如,<div />
代表 HTML 的 div 标签,而 <Welcome />
则代表一个组件,并且需在作用域内使用 Welcome
。
2、组件可以接受任意 props,包括基本数据类型,React 元素以及函数。
如果你想要在组件间复用非 UI 的功能,我们建议将其提取为一个单独的 JavaScript 模块,如函数、对象或者类。组件可以直接引入(import)而无需通过 extend 继承它们。
3、你不能在函数组件上使用 ref
属性,因为他们没有实例。
4、React 的 diff 算法(称为协调)使用组件标识来确定它是应该更新现有子树还是将其丢弃并挂载新子树。 如果从 render
返回的组件与前一个渲染中的组件相同(===
),则 React 通过将子树与新子树进行区分来递归更新子树。 如果它们不相等,则完全卸载前一个子树。