State
Props 的只读性
数据的单向传递,任何情况下都不能在子组件中修改props。⭐
正确地使用 State
- 不要直接修改 State ```javascript // Wrong this.state.comment = ‘Hello’;
// Correct this.setState({comment: ‘Hello’});
2. State 的更新可能是异步的> 出于性能考虑,React 可能会把多个 setState() 调用合并成一个调用。因为 this.props 和 this.state 可能会异步更新,所以你不要依赖他们的值来更新下一个状态。```javascript// Wrongthis.setState({counter: this.state.counter + this.props.increment,});// Correctthis.setState((state, props) => ({counter: state.counter + props.increment}));
- React 事件的命名采用小驼峰式(camelCase),而不是纯小写。
- 使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。
<button onClick={activateLasers}>Activate Lasers</button>
你必须谨慎对待 JSX 回调函数中的 this,在 JavaScript 中,class 的方法默认不会绑定 this。如果你忘记绑定 this.handleClick 并把它传入了 onClick,当你调用这个函数的时候 this 的值为 undefined。
有三种方法可以绑定this
箭头函数
class LoggingButton extends React.Component {// 此语法确保 `handleClick` 内的 `this` 已被绑定。// 注意: 这是 *实验性* 语法。handleClick = () => {console.log('this is:', this);}render() {return (<button onClick={this.handleClick}>Click me</button>);}}
回调中使用箭头函数
class LoggingButton extends React.Component {handleClick() {console.log('this is:', this);}render() {// 此语法确保 `handleClick` 内的 `this` 已被绑定。return (<button onClick={() => this.handleClick()}>Click me</button>);}}
显式绑定
class Toggle extends React.Component {constructor(props) {super(props);this.state = {isToggleOn: true};// 为了在回调中使用 `this`,这个绑定是必不可少的this.handleClick = this.handleClick.bind(this);}handleClick() {this.setState(prevState => ({isToggleOn: !prevState.isToggleOn}));}render() {return (<button onClick={this.handleClick}>{this.state.isToggleOn ? 'ON' : 'OFF'}</button>);}}
向事件处理程序传递参数
在循环中,通常我们会为事件处理函数传递额外的参数。
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button><button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
key
组合 vs 继承
React中的slot
props.children```jsx function FancyBorder(props) { return ({props.children}); }
function WelcomeDialog() {
return (
Welcome
Thank you for visiting our spacecraft!
); }
<a name="GdHyy"></a>### 具名插槽通过props传递即可,在react中万物皆可传。```jsxfunction SplitPane(props) {return (<div className="SplitPane"><div className="SplitPane-left">{props.left}</div><div className="SplitPane-right">{props.right}</div></div>);}function App() {return (<SplitPaneleft={<Contacts />}right={<Chat />} />);}
高级指引
Fragment
类似vue中的template,但是react中可以设置为<></>.
React.lazy
与vue3中的类似,结合Suspense组件可以实现异步加载组件。
import React, { Suspense } from 'react';const OtherComponent = React.lazy(() => import('./OtherComponent'));const AnotherComponent = React.lazy(() => import('./AnotherComponent'));function MyComponent() {return (<div><Suspense fallback={<div>Loading...</div>}><section><OtherComponent /><AnotherComponent /></section></Suspense></div>);}
异常捕获边界(Error boundaries)
import React, { Suspense } from 'react';import MyErrorBoundary from './MyErrorBoundary';const OtherComponent = React.lazy(() => import('./OtherComponent'));const AnotherComponent = React.lazy(() => import('./AnotherComponent'));const MyComponent = () => (<div><MyErrorBoundary><Suspense fallback={<div>Loading...</div>}><section><OtherComponent /><AnotherComponent /></section></Suspense></MyErrorBoundary></div>);
基于路由的代码分割
import React, { Suspense, lazy } from 'react';import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';const Home = lazy(() => import('./routes/Home'));const About = lazy(() => import('./routes/About'));const App = () => (<Router><Suspense fallback={<div>Loading...</div>}><Switch><Route exact path="/" component={Home}/><Route path="/about" component={About}/></Switch></Suspense></Router>);
Context⭐
类似于vue的provide
主要API:
- React.createContext() =>第一参数是默认值
- Context.Provider
<MyContext.Provider value={/* 某个值 */}>
Context.Consumer
<ThemeContext.Consumer>{({theme, toggleTheme}) => (<buttononClick={toggleTheme}style={{backgroundColor: theme.background}}>Toggle Theme</button>)}</ThemeContext.Consumer>
Context.displayName
context 对象接受一个名为 displayName 的 property,类型为字符串。React DevTools 使用该字符串来确定 context 要显示的内容。
示例,下述组件在 DevTools 中将显示为 MyDisplayName:
const MyContext = React.createContext(/* some value */);MyContext.displayName = 'MyDisplayName';<MyContext.Provider> // "MyDisplayName.Provider" 在 DevTools 中<MyContext.Consumer> // "MyDisplayName.Consumer" 在 DevTools 中
// Context 可以让我们无须明确地传遍每一个组件,就能将值深入传递进组件树。// 为当前的 theme 创建一个 context(“light”为默认值)。const ThemeContext = React.createContext('light');class App extends React.Component {render() {// 使用一个 Provider 来将当前的 theme 传递给以下的组件树。// 无论多深,任何组件都能读取这个值。// 在这个例子中,我们将 “dark” 作为当前的值传递下去。return (<ThemeContext.Provider value="dark"><Toolbar /></ThemeContext.Provider>);}}// 中间的组件再也不必指明往下传递 theme 了。function Toolbar() {return (<div><ThemedButton /></div>);}class ThemedButton extends React.Component {// 指定 contextType 读取当前的 theme context。// React 会往上找到最近的 theme Provider,然后使用它的值。// 在这个例子中,当前的 theme 值为 “dark”。static contextType = ThemeContext;render() {return <Button theme={this.context} />;}}
Context 主要应用场景在于很多不同层级的组件需要访问同样一些的数据。请谨慎使用,因为这会使得组件的复用性变差。
如果你只是想避免层层传递一些属性,组件组合(component composition)有时候是一个比 context 更好的解决方案。
错误边界
如果一个 class 组件中定义了 static getDerivedStateFromError() 或 componentDidCatch() 这两个生命周期方法中的任意一个(或两个)时,那么它就变成一个错误边界。当抛出错误后,请使用 static getDerivedStateFromError() 渲染备用 UI ,使用 componentDidCatch() 打印错误信息。
class ErrorBoundary extends React.Component {constructor(props) {super(props);this.state = { hasError: false };}static getDerivedStateFromError(error) {// 更新 state 使下一次渲染能够显示降级后的 UIreturn { hasError: true };}componentDidCatch(error, errorInfo) {// 你同样可以将错误日志上报给服务器logErrorToMyService(error, errorInfo);}render() {if (this.state.hasError) {// 你可以自定义降级后的 UI 并渲染return <h1>Something went wrong.</h1>;}return this.props.children;}}
<ErrorBoundary><MyWidget /></ErrorBoundary>
Refs 转发
没看懂 , 需要加深理解,链接
shouldComponentUpdate
class CounterButton extends React.Component {constructor(props) {super(props);this.state = {count: 1};}shouldComponentUpdate(nextProps, nextState) {if (this.props.color !== nextProps.color) {return true;}if (this.state.count !== nextState.count) {return true;}return false;}render() {return (<buttoncolor={this.props.color}onClick={() => this.setState(state => ({count: state.count + 1}))}>Count: {this.state.count}</button>);}}
在这段代码中,shouldComponentUpdate 仅检查了 props.color 或 state.count 是否改变。如果这些值没有改变,那么这个组件不会更新。如果你的组件更复杂一些,你可以使用类似“浅比较”的模式来检查 props 和 state 中所有的字段,以此来决定是否组件需要更新。React 已经提供了一位好帮手来帮你实现这种常见的模式 - 你只要继承 React.PureComponent 就行了。所以这段代码可以改成以下这种更简洁的形式:
class CounterButton extends React.PureComponent {constructor(props) {super(props);this.state = {count: 1};}render() {return (<buttoncolor={this.props.color}onClick={() => this.setState(state => ({count: state.count + 1}))}>Count: {this.state.count}</button>);}}
Portals
类似于vue3的传送门Teleport
render() {// React 并*没有*创建一个新的 div。它只是把子元素渲染到 `domNode` 中。// `domNode` 是一个可以在任何位置的有效 DOM 节点。return ReactDOM.createPortal(this.props.children,domNode);}
Profiler API
render(<App><Profiler id="Navigation" onRender={callback}><Navigation {...props} /></Profiler><Main {...props} /></App>);
onRender
function onRenderCallback(id, // 发生提交的 Profiler 树的 “id”phase, // "mount" (如果组件树刚加载) 或者 "update" (如果它重渲染了)之一actualDuration, // 本次更新 committed 花费的渲染时间baseDuration, // 估计不使用 memoization 的情况下渲染整颗子树需要的时间startTime, // 本次更新中 React 开始渲染的时间commitTime, // 本次更新中 React committed 的时间interactions // 属于本次更新的 interactions 的集合) {// 合计或记录渲染时间。。。}
