createPortal 的出现为 弹窗、对话框 等脱离文档流的组件开发提供了便利,Portals 提供了一种很好的将子节点渲染到父组件以外的 DOM 节点的方式。
const modalRoot = document.body;
class Modal extends React.Component {
constructor(props) {
super(props);
this.el = document.createElement('div');
}
componentDidMount() {
modalRoot.appendChild(this.el);
}
componentWillUnmount() {
modalRoot.removeChild(this.el);
}
render() {
return ReactDOM.createPortal(
this.props.children,
this.el,
);
}
}
render方法支持直接返回string,number,boolean,null,portal,以及fragments(带有key属性的数组),这可以在一定程度上减少页面的DOM层级
// 不需要再将元素作为子元素装载到根元素下面
render() {
return [
一步 01,
一步 02,
一步 03,
一步 04
];
}
static静态方法,在es5中这么实现:
function Person() {}
Person.getCount = function () {}
以上就是static静态方法的原理。由于“this”只能获取属性是根据原型链,而静态方法不在原型链上,所以,在组件实例内无通过this调用static方法,static方法也无法根据"this"调用实例的其他方法。
静态方法和动态方法区别
static getDerivedStateFromProps(nextProps, prevState) {
const {type} = nextProps;
// 当传入的type发生变化的时候,更新state
if (type !== prevState.type) {
return {
type,
};
}
// 否则,对于state不进行任何操作
return null;
}
// 你可能会用以下这样做,虽然这样做看起来也没问题,用上面的方法更加安全,不会对this做误操作
componentWillReceiveProps (nextProps) {
if (this.state.name !== nextProps.name) {
this.setState({
name: nextProps.name
});
}
}
如果错误在组件的渲染或者生命周期方法中被抛出,则会触发该函数。
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
componentDidCatch(error, info) {
this.setState({ hasError: true });
}
render() {
if (this.state.hasError) {
return Something went wrong.
;
}
return this.props.children;
}
}
然后在顶部或任何地方,你可以这样使用它
React.lazy() 提供了动态 import 组件的能力,实现代码分割。
Suspense 作用是在等待组件时 suspend(暂停)渲染,并显示加载标识。
import React, {lazy, Suspense} from 'react';
const OtherComponent = lazy(() => import('./OtherComponent'));
function MyComponent() {
return (
Loading...
如果不喜欢写数组呢,可以使用这个(简写<>>),这样并不会在DOM中增加额外节点,相当于 render 返回数组元素。
return (
一步 01
一步 02
一步 03
一步 04
);
React16中有两种创建Ref的方法
constructor () {
this.inputNameRef = React.createRef();
this.switchRef = React.createRef();
}
render () {
// 通过this.inputNameRef.current 可以获取到input实例
return (
)
}
render () {
// 通过回调ref的方式实现
// 通过this.inputNameRef 可以获取到input实例
// this.switchRef可以获取Switch的实例
return (
this.inputNameRef = ref} />
this.switchRef = ref} />
)
}
// 传入初始值,作为 state
const [state, setState] = useState(initialState)
// `惰性初始 state`;传入函数,由函数计算出的值作为 state
// 此函数只在初始渲染时被调用
const [state, setState] = useState(() => {
const initialState = someExpensiveComputation(props)
return initialState
})
以下就是一个创建订阅的例子:
useEffect(() => {
const subscription = props.source.subscribe()
return () => {
// 清除订阅
subscription.unsubscribe()
}
}, [依赖])
不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层调用他们。
(*推荐启用 eslint-plugin-react-hooks 的 ESLint 插件)
useCallback和useMemo的参数跟useEffect一致,他们之间最大的区别有是useEffect会用于处理副作用,而前两个hooks不能。
useMemo和useCallback都会在组件第一次渲染的时候执行,之后会在其依赖的变量发生改变时再次执行;并且这两个hooks都返回缓存的值,useMemo返回缓存的变量,useCallback返回缓存的函数。
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);