You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
...
在严格模式下使用 React 18,`useEffect() that should run only on the initial mount, runs twice!`
...
---
...
React 18 为严格模式引入了一个新的仅限开发的检查。每当第一次安装组件时,此新检查将自动卸载并重新安装每个组件,并在第二次安装时恢复先前的状态。
...
Before this change
* React mounts the component.
* Layout effects are created.
* Effect effects are created.
With Strict Mode in React 18
* React mounts the component.
* Layout effects are created.
* Effect effects are created.
* React simulates unmounting the component.
* Layout effects are destroyed.
* Effects are destroyed.
* React simulates mounting the component with the previous state.
* Layout effect setup code runs
* Effect setup code runs
// Inserted by a compiler (don't import it yourself!)import{jsxas_jsx}from'react/jsx-runtime';functionApp(){return_jsx('h1',{children: 'Hello world'});}
exportconstREACT_ELEMENT_TYPE=Symbol.for("react.element");import{REACT_ELEMENT_TYPE}from"./shared/ReactSymbols";constRESERVED_PROPS={key: true,ref: true,__self: true,__source: true,};/** * Factory method to create a new React element. This no longer adheres to * the class pattern, so do not use new to call it. Also, instanceof check * will not work. Instead test $$typeof field against Symbol.for('react.element') to check * if something is a React Element. * * @param {*} type * @param {*} props * @param {*} key * @param {string|object} ref * @param {*} owner * @param {*} self A *temporary* helper to detect places where `this` is * different from the `owner` when React.createElement is called, so that we * can warn. We want to get rid of owner and replace string `ref`s with arrow * functions, and as long as `this` and owner are the same, there will be no * change in behavior. * @param {*} source An annotation object (added by a transpiler or otherwise) * indicating filename, line number, and/or other information. * @internal */constReactElement=function(type,key,ref,self,source,owner,props){constelement={// This tag allows us to uniquely identify this as a React Element$$typeof: REACT_ELEMENT_TYPE,// Built-in properties that belong on the elementtype: type,key: key,ref: ref,props: props,// Record the component responsible for creating this element._owner: owner,_store: {},_self: self,_source: source,};returnelement;};exportfunctioncreateElement(type,config,children){// Reserved names are extractedconstprops={};letkey=null;letref=null;letself=null;letsource=null;if(config!=null){key=""+config.key;self=config.__self===undefined ? null : config.__self;source=config.__source===undefined ? null : config.__source;// Remaining properties are added to a new props objectfor(constproNameinconfig){if(Object.hasOwnProperty.call(config,proName)&&!RESERVED_PROPS.hasOwnProperty(proName)){props[proName]=config[proName];}}}// Children can be more than one argument, and those are transferred onto// the newly allocated props object.constchildrenLength=arguments.length-2;if(childrenLength===1){props.children=children;}elseif(childrenLength>1){constchildArray=Array(childrenLength);for(leti=0;i<childArray.length;i++){childArray[i]=arguments[i+2];}props.children=childArray;}const_owner=null;returnReactElement(type,key,ref,self,source,_owner,props);}
constemptyObject={};/** * Base class helpers for the updating state of a component. */exportfunctionComponent(props){this.props=props;this.refs=emptyObject;}Component.prototype.isReactComponent={};
import{REACT_ELEMENT_TYPE}from'../../shared/ReactSymbols';consthasOwnProperty=Object.prototype.hasOwnProperty;constRESERVED_PROPS={key: true,ref: true,__self: true,__source: true,};/** * Create and return a new ReactElement of the given type. * See https://reactjs.org/docs/react-api.html#createelement */exportfunctioncreateElement(type: any,config: any,children: any){/** * propName -> 属性名称 * 用于后面的 for 循环 */letpropName: any;/** * 存储 React Element 中的普通元素属性 即不包含 key ref self source */// Reserved names are extractedconstprops: {[key: string]: any}={};/** * 待提取属性 * React 内部为了实现某些功能而存在的属性 */letkey=null;letref=null;letself=null;letsource=null;// 如果 config 不为 nullif(config!==null){// 如果 config 对象中有合法的 ref 属性if(hasValidRef(config)){// 将 config.ref 属性提取到 ref 变量中ref=config.ref;}// 如果在 config 对象中拥有合法的 key 属性if(hasValidKey(config)){// 将 config.key 属性中的值提取到 key 变量中key=''+config.key;}self=config.__self===undefined ? null : config.__self;source=config.__source===undefined ? null : config.__source;// 遍历 config 对象// Remaining properties are added to a new props objectfor(propNameinconfig){// 如果当前遍历到的属性是对象自身属性// 并且在 RESERVED_PROPS 对象中不存在该属性if(hasOwnProperty.call(config,propName)&&!RESERVED_PROPS.hasOwnProperty(propName)){// 将满足条件的属性添加到 props 对象中 (普通属性)props[propName]=config[propName];}}}/** * 将第三个及之后的参数挂载到 props.children 属性中 * 如果子元素是多个 props.children 是数组 * 如果子元素是一个 props.children 是对象 */// 由于从第三个参数开始及以后都表示子元素// 所以减去前两个参数的结果就是子元素的数量// Children can be more than one argument, and those are transferred onto// the newly allocated props object.constchildrenLength=arguments.length-2;// 如果子元素的数量是 1if(childrenLength===1){// 直接将子元素挂载到到 props.children 属性上// 此时 children 是对象类型props.children=children;// 如果子元素的数量大于 1}elseif(childrenLength>1){// 创建数组, 数组中元素的数量等于子元素的数量constchildArray=Array(childrenLength);// 开启循环 循环次匹配子元素的数量for(leti=0;i<childrenLength;i++){// 将子元素添加到 childArray 数组中// i + 2 的原因是实参集合的前两个参数不是子元素childArray[i]=arguments[i+2];}// 将子元素数组挂载到 props.children 属性中props.children=childArray;}/** * 如果当前处理是组件 * 看组件身上是否有 defaultProps 属性 * 这个属性中存储的是 props 对象中属性的默认值 * 遍历 defaultProps 对象 查看对应的 props 属性的值是否为 undefined * 如果为undefined 就将默认值赋值给对应的 props 属性值 */// Resolve default propsif(type&&type.defaultProps){constdefaultProps=type.defaultProps;for(propNameindefaultProps){if(props[propName]===undefined){props[propName]=defaultProps[propName];}}}returnReactElement(type,key,ref,self,source,null,// ReactCurrentOwner.current,props);}
ReactElement
// 接收参数 返回 ReactElement/** * Factory method to create a new React element. This no longer adheres to * the class pattern, so do not use new to call it. Also, instanceof check * will not work. Instead test $$typeof field against Symbol.for('react.element') to check * if something is a React Element. * * @param {*} type * @param {*} props * @param {*} key * @param {string|object} ref * @param {*} owner * @param {*} self A *temporary* helper to detect places where `this` is * different from the `owner` when React.createElement is called, so that we * can warn. We want to get rid of owner and replace string `ref`s with arrow * functions, and as long as `this` and owner are the same, there will be no * change in behavior. * @param {*} source An annotation object (added by a transpiler or otherwise) * indicating filename, line number, and/or other information. * @internal */constReactElement=function(type: any,key: any,ref: any,self: any,source: any,owner: any,props: any){constelement={/** * 组件的类型, 十六进制数值或者 Symbol 值 * React 在最终在渲染 DOM 的时候, 需要确保元素的类型是 REACT_ELEMENT_TYPE * 需要此属性作为判断的依据 */// This tag allows us to uniquely identify this as a React Element$$typeof: REACT_ELEMENT_TYPE,/** * 元素具体的类型值 如果是元素节点 type 属性中存储的就是 div span 等等 * 如果元素是组件 type 属性中存储的就是组件的构造函数 */// Built-in properties that belong on the elementtype: type,/** * 元素的唯一标识 * 用作内部 vdom 比对 提升 DOM 操作性能 */key: key,/** * 存储元素 DOM 对象或者组件 实例对象 */ref: ref,/** * 存储向组件内部传递的数据 */props: props,/** * 记录当前元素所属组件 (记录当前元素是哪个组件创建的) */// Record the component responsible for creating this element._owner: owner,};// 返回 ReactElementreturnelement;};
/** * 验证 object 参数是否是 ReactElement. 返回布尔值 * 验证成功的条件: * object 是对象 * object 不为null * object对象中的 $$typeof 属性值为 REACT_ELEMENT_TYPE * * Verifies the object is a ReactElement. * See https://reactjs.org/docs/react-api.html#isvalidelement * @param {?object} object * @return {boolean} True if `object` is a ReactElement. * @final */exportfunctionisValidElement(object: {[key: string]: any}){return(typeofobject==='object'&&object!==null&&object.$$typeof===REACT_ELEMENT_TYPE);}
defineKeyPropWarningGetter
defineRefPropWarningGetter
letspecialPropKeyWarningShown: boolean;letspecialPropRefWarningShown: boolean;/** * 指定当通过 props 对象获取 key 属性时报错 * props 组件中的 props 对象 * displayName 组件名称标识 */functiondefineKeyPropWarningGetter(props: {[key: string]: any},displayName: string){// 通过 props 对象获取 key 属性报错constwarnAboutAccessingKey=function(){// 在开发环境中if(__DEV__){// specialPropKeyWarningShown 控制错误只输出一次的变量if(!specialPropKeyWarningShown){// 通过 specialPropKeyWarningShown 变量锁住判断条件specialPropKeyWarningShown=true;// 指定报错信息和组件名称console.error('%s: `key` is not a prop. Trying to access it will result '+'in `undefined` being returned. If you need to access the same '+'value within the child component, you should pass it as a different '+'prop. (https://fb.me/react-special-props)',displayName);}}};warnAboutAccessingKey.isReactWarning=true;// 为 props 对象添加 key 属性Object.defineProperty(props,'key',{// 当获取 key 属性时调用 warnAboutAccessingKey 方法进行报错get: warnAboutAccessingKey,configurable: true,});}/** * 指定当通过 props 对象获取 ref 属性时报错 * props 组件中的 props 对象 * displayName 组件名称标识 */functiondefineRefPropWarningGetter(props: {[key: string]: any},displayName: string){// 通过 props 对象获取 ref 属性报错constwarnAboutAccessingRef=function(){if(__DEV__){// specialPropRefWarningShown 控制错误只输出一次的变量if(!specialPropRefWarningShown){// 通过 specialPropRefWarningShown 变量锁住判断条件specialPropRefWarningShown=true;// 指定报错信息和组件名称console.error('%s: `ref` is not a prop. Trying to access it will result '+'in `undefined` being returned. If you need to access the same '+'value within the child component, you should pass it as a different '+'prop. (https://fb.me/react-special-props)',displayName);}}};warnAboutAccessingRef.isReactWarning=true;// 为 props 对象添加 key 属性Object.defineProperty(props,'ref',{get: warnAboutAccessingRef,configurable: true,});}constprops={ref: 'ref',key: 'key'};defineKeyPropWarningGetter(props,'key');defineRefPropWarningGetter(props,'ref');props.key;props.ref;// key: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop.// ref: `ref` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop.
Render
/** * 渲染入口 * element 要进行渲染的 ReactElement, createElement 方法的返回值 * container 渲染容器 <div id="root"></div> * callback 渲染完成后执行的回调函数 */exportfunctionrender(element: any,container: Container,callback?: Function){console.log('render',element,container,callback);// 检测 container 是否是符合要求的渲染容器// 即检测 container 是否是真实的DOM对象// 如果不符合要求就报错invariant(isValidContainer(container),'Target container is not a DOM element.');returnlegacyRenderSubtreeIntoContainer(// 父组件 初始渲染没有父组件 传递 null 占位null,element,container,// 是否为服务器端渲染 false 不是服务器端渲染 true 是服务器端渲染false,callback);}
constcreateFiber=function(tag: WorkTag,pendingProps: any,key: null|string,mode: TypeOfMode,): Fiber{// $FlowFixMe: the shapes are exact here but Flow doesn't like constructorsreturnnew(FiberNodeasany)(tag,pendingProps,key,mode);};
useEffect 在组件挂载时运行两次(StrictMode,NODE_ENV=development)
Before this change
With Strict Mode in React 18
-facebook/react#24502
-https://reactjs.org/blog/2022/03/08/react-18-upgrade-guide.html#updates-to-strict-mode
-https://levelup.gitconnected.com/react-18-the-trickiness-of-useeffect-fadfa65fa4b4
-https://stackoverflow.com/questions/72238175/react-18-useeffect-is-getting-called-two-times-on-mount
useEffect 依赖项
new Map 会一直改变
源码
介绍全新的 JSX 转换 v17.0 RC
https://zh-hans.reactjs.org/blog/2020/08/10/react-v17-rc.html
https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html
旧的 JSX 转换将 JSX 转换为React.createElement(...)调用,
新的 JSX 转换不是将 JSX 转换为React.createElement,而是自动从 React 包中的这些新入口点导入特殊函数并调用它们。
React16废弃了哪些生命周期?为什么?
React16废弃的生命周期有3个will:
componentWillMount
componentWillReceiveProps
componentWillUpdate
v16.13.1
createElement
ReactElement
hasValidRef
hasValidKey
isValidElement
defineKeyPropWarningGetter
defineRefPropWarningGetter
Render
isValidContainer
react-mount-point-unstable
Support rendering into comment facebook/react#17547
https://codesandbox.io/s/react-comment-node-as-root-element-2bqz8?file=/src/index.js:179-197
isValidContainer
render
legacyCreateRootFromDOMContainer
createLegacyRoot
ReactDOMBlockingRoot
createRootImpl
createContainer
createFiberRoot
FiberRootNode
createHostRootFiber
createFiber
FiberNode
The text was updated successfully, but these errors were encountered: