Skip to content

Commit

Permalink
Updates to work with React 19
Browse files Browse the repository at this point in the history
  • Loading branch information
tschaub committed Dec 27, 2024
1 parent a343f88 commit cab1ef4
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 28 deletions.
3 changes: 2 additions & 1 deletion lib/Map.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import OLMap from 'ol/Map.js';
import propTypes from 'prop-types';
import {Component, createElement, createRef, forwardRef} from 'react';
import {MAP} from './internal/config.js';
import {render, updateInstanceFromProps} from './internal/render.js';

const defaultDivStyle = {
Expand All @@ -38,7 +39,7 @@ class Map extends Component {
innerRef.current = this.map;
}
}
updateInstanceFromProps(this.map, mapProps);
updateInstanceFromProps(this.map, MAP, {}, mapProps);
}

componentDidMount() {
Expand Down
1 change: 1 addition & 0 deletions lib/internal/config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/**
* The constants below represent the primitive element types handled by the renderer.
*/
export const MAP = 'map';
export const VIEW = 'view';
export const OVERLAY = 'overlay';
export const CONTROL = 'control';
Expand Down
123 changes: 103 additions & 20 deletions lib/internal/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {CONTROL, INTERACTION, LAYER, OVERLAY, SOURCE, VIEW} from './config.js';
import {
ConcurrentRoot,
DefaultEventPriority,
NoEventPriority,
} from 'react-reconciler/constants.js';
import {
prepareControlUpdate,
Expand Down Expand Up @@ -41,37 +42,73 @@ const knownTypes = {
[SOURCE]: true,
};

export function updateInstanceFromProps(instance, props, oldProps = {}) {
for (const key in props) {
/**
* @param {Array} a1 An array.
* @param {Array} a2 An array.
* @return {boolean} All elements in the array are the same;
*/
function arrayEquals(a1, a2) {
if (!a1 || !a2) {
return false;
}
const len1 = a1.length;
const len2 = a2.length;
if (len1 !== len2) {
return false;
}
for (let i = 0; i < len1; i += 1) {
const v1 = a1[i];
const v2 = a2[i];
if (v1 !== v2) {
return false;
}
}
return true;
}

export function updateInstanceFromProps(instance, type, oldProps, newProps) {
for (const key in newProps) {
if (reservedProps[key]) {
continue;
}
const newValue = newProps[key];
const oldValue = oldProps[key];
if (oldValue === newValue) {
continue;
}

if (listenerRegex.test(key)) {
const listener = props[key];
const type = key
const listener = newProps[key];
const eventType = key
.replace(listenerColonRegex, 'onChange:')
.replace(listenerRegex, '$1')
.toLowerCase();
instance.on(type, listener);
if (oldProps[key]) {
instance.un(type, oldProps[key]);
instance.on(eventType, listener);

const oldListener = oldProps[key];
if (oldListener) {
instance.un(eventType, oldListener);
if (instance.changed) {
instance.changed();
}
}
continue;
}

if (key === 'center' && arrayEquals(newValue, oldValue)) {
continue;
}

const setter = setterName(key);
if (typeof instance[setter] === 'function') {
instance[setter](props[key]);
instance[setter](newValue);
continue;
}

if (key === 'features' && typeof instance.addFeatures === 'function') {
// TODO: there is likely a smarter way to diff features
instance.clear(true);
instance.addFeatures(props[key]);
instance.addFeatures(newValue);
continue;
}

Expand All @@ -80,12 +117,12 @@ export function updateInstanceFromProps(instance, props, oldProps = {}) {
typeof instance.addInteraction === 'function'
) {
instance.getInteractions().clear();
props[key].forEach(interaction => instance.addInteraction(interaction));
newValue.forEach(interaction => instance.addInteraction(interaction));
continue;
}
if (key === 'controls' && typeof instance.addControl === 'function') {
instance.getControls().clear();
props[key].forEach(control => instance.addControl(control));
newValue.forEach(control => instance.addControl(control));
continue;
}

Expand All @@ -102,7 +139,7 @@ function createInstance(type, {cls: Constructor, ...props}) {
}

const instance = new Constructor(props.options || {});
updateInstanceFromProps(instance, props);
updateInstanceFromProps(instance, type, {}, props);
return instance;
}

Expand Down Expand Up @@ -167,8 +204,8 @@ function prepareUpdate(instance, type, oldProps, newProps) {
return updater(instance, type, oldProps, newProps);
}

function commitUpdate(instance, updatePayload, type, oldProps) {
updateInstanceFromProps(instance, updatePayload, oldProps);
function commitUpdate(instance, type, oldProps, newProps) {
updateInstanceFromProps(instance, type, oldProps, newProps);
}

function removeChildFromContainer(map, child) {
Expand Down Expand Up @@ -247,6 +284,9 @@ function insertBefore(parent, child, beforeChild) {
throw new Error(`Cannot insert child ${child} into parent ${parent}`);
}

const noContext = {};
let currentUpdatePriority = NoEventPriority;

const reconciler = ReactReconciler({
supportsMutation: true,
isPrimaryRenderer: false,
Expand All @@ -260,21 +300,66 @@ const reconciler = ReactReconciler({
clearContainer,
removeChildFromContainer,
removeChild,

insertInContainerBefore,
insertBefore,
noTimeout: -1,

getInstanceFromNode() {
return null;
},

shouldAttemptEagerTransition() {
return false;
},

requestPostPaintCallback() {},

maySuspendCommit() {
return false;
},

preloadInstance() {
return true;
},

startSuspendingCommit() {},

suspendInstance() {},

waitForCommitToBeReady() {
return null;
},

setCurrentUpdatePriority(newPriority) {
currentUpdatePriority = newPriority;
},

getCurrentUpdatePriority() {
return currentUpdatePriority;
},

resolveUpdatePriority() {
if (currentUpdatePriority) {
return currentUpdatePriority;
}
return DefaultEventPriority;
},

finalizeInitialChildren() {
return false;
},

getChildHostContext() {},
getChildHostContext() {
return noContext;
},

getPublicInstance(instance) {
return instance;
},

getRootHostContext() {},
getRootHostContext() {
return noContext;
},

getCurrentEventPriority() {
return DefaultEventPriority;
Expand All @@ -299,9 +384,7 @@ export function render(element, container) {
let root = roots.get(container);
if (!root) {
const logRecoverableError =
typeof reportError === 'function'
? reportError // eslint-disable-line no-undef
: console.error; // eslint-disable-line no-console
typeof reportError === 'function' ? reportError : console.error; // eslint-disable-line no-console

root = reconciler.createContainer(
container,
Expand Down
1 change: 1 addition & 0 deletions lib/internal/update.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const reservedProps = {
children: true,
cls: true,
options: true,
ref: true, // TODO: deal with changing ref
};

export function prepareViewUpdate(view, type, oldProps, newProps) {
Expand Down
16 changes: 10 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"@playwright/test": "^1.25.2",
"@testing-library/react": "^16.0.0",
"@types/react": "^19.0.2",
"@types/react-dom": "^18.0.10",
"@types/react-dom": "^19.0.2",
"@vitest/browser": "^2.0.3",
"astro": "^4.0.3",
"es-main": "^1.2.0",
Expand Down

0 comments on commit cab1ef4

Please sign in to comment.