Wijmo has made significant improvements to React interop in order to support functional programming and Next.js support. Some customers might encounter breaking changes due to these updates. Previously, we migrated all our React samples to functional (from class-based). We have continued to refine Wijmo in functional React and are also announcing Next.js support!
Why Did We Make These Changes?
All of these changes to our React interop were made in order to improve Wijmo in React. The benefits of these changes are as follows:
- Functional Components: Functional components offer several advantages over class components, including simpler syntax, improved readability, and easier understanding of component behavior.
- Native Event Handler Support: Functional components seamlessly support React-style event handlers, enabling the addition of events without the need for additional hooks, like useEvent. This simplifies event handling and reduces boilerplate code.
- Enhanced TypeScript Support: Functional components provide better TypeScript support, facilitating type checking and ensuring more robust code. This leads to fewer runtime errors and improved code quality.
- Strict type checks for properties
- Support for string values in Enum type properties
- Compatibility with Next.js: Migrating to functional components makes Wijmo compatible with Next.js.
- Improved Closure for Event Handlers: Functional components offer better closure for event handlers, aligning more closely with React's usage patterns. This enhances code maintainability and reduces the risk of unexpected behavior related to event handling.
Managing JSX Defaults and Dynamic Updates Using strictStateMode()
The strictStateMode option has been added to wijmo.react.base. By default, this option is set to false to maintain compatibility with existing user applications. If users want to utilize the Wijmo API in accordance with React's state management philosophy, they can set strictStateMode to true. So, you will need to opt-in to this new behavior.
Using strictStateMode(true):
import { strictStateMode } from "@mescious/wijmo.react.base";
strictStateMode(true);
Using strictStateMode(true); will result in the following breaking changes:
Key Changes
- Default Value Handling in JSX: React applications relying on previous behaviors to set default values in JSX may encounter challenges when attempting to update values using TypeScript or JavaScript directly.
- Dynamic Sheet Addition in FlexSheet: Control state updates occurring in each render cycle can impact the addition of dynamic sheets in FlexSheet, influencing application behavior.
- Column Ordering in FlexGrid: JSX synchronization affects column ordering, especially when columns are initially defined using JSX, which can lead to unexpected behavior in column layout.
Breaking Changes
While this refactoring brings many benefits, it might also cause errors in your app. Below, we highlight the issues you might encounter and their solutions.
TypeScript projects using React interop may experience compilation errors related to JSX syntax if relying on non-self-closing JSX components
React interop is now strictly typed, so depending on the build setup, type errors may prevent the project from building until all errors are resolved.
Most of the time, type errors are simple to fix. We just need to assign the correct types to the properties.
However, one error might seem like a mistake and could be more complex to fix.
In the above error, the TypeScript compiler tells us that we cannot assign children to FlexChartLegend.
At first glance, it might seem like we haven't assigned any children. So, why is TSC throwing the error? If you look carefully, there's a space between the opening and closing tags of FlexChartLegend, and TSX treats that space as a child element, which causes the error.
Solution
To fix this, be sure to use self-closing TSX tags to avoid any hidden children.
Replaced class component types with functional component ref types (e.g., FlexGrid to FlexGridRef)
In earlier versions, leveraging class components allowed direct usage of the components themselves as types. However, with the transition to functional components, this approach is no longer feasible. To address this, we've implemented ref types for each component. These ref types bear the same name as the control, suffixed by "Ref." For instance, if the control name is FlexGrid, its corresponding ref type would be FlexGridRef.
Take note of the type utilized with the gridRef ref in the following code snippet:
Before 2024 v1 Hotfix:
import {FlexGrid} from "@mescius/wijmo.react.grid";
import {useRef} from "react";
function App(){
const gridRef = useRef<FlexGrid>();
const data = useState(getDummyData());
return (
<FlexGrid
ref = {gridRef}
itemsSource = {data}
/>
);
}
2024 v1 Hotfix:
import {FlexGrid, FlexGridRef} from "@mescius/wijmo.react.grid";
import {useRef} from "react";
function App(){
const gridRef = useRef<FlexGridRef>();
const data = useState(getDummyData());
return (
<FlexGrid
ref = {gridRef}
itemsSource = {data}
/>
);
}
React apps relying on the previous behavior to set default values in JSX may find it harder to change values using TS/JS code directly
NOTE: This is only applicable when setting strictStateMode(true); By default, this will not be a breaking change in customers' applications.
In previous versions of React interop, the values provided to the control via JSX were not synced on each render cycle. They would only be applied if the provided value changed. This allowed you to initially set a value via JSX and then change its value using JS/TS code, which was incorrect behavior.
According to React philosophy, the render state should always reflect the provided state.
In the latest version, this has been improved. Now, the values provided to the component via JSX/TSX are always synced with the rendered control. If you set a value via JSX, it is not possible to change the value at runtime without updating the state.
In the above example, we assign default values to two different properties: allowResizing and allowSorting.
At first glance, both seem identical, but there is one major difference: allowResizing is assigned a false value directly, whereas, in the case of allowSorting, a state variable is used.
This difference makes it possible for us to change the value of allowSorting at runtime, whereas the value of allowResizing cannot be changed at runtime and will always remain false.
Solution
- If you need to change the values at runtime, use state variables to assign values
- Instead of assigning values via JSX, use the initialized event and set the default values once so they won’t be synched on each render cycle
- To set default values for multiple controls at once, we may also use the useEffect hook as shown below:
[React][TreeView][Accordion] TabPanel and Accordion components may experience layout inconsistencies or unexpected styling due to the introduction of the wrapping <div> element
React interop for Tab and AccordionPane components, where previously provided HTML content was directly added to the control body, is now wrapped inside a div element.
This alteration may impact CSS styles that are reliant on the original structure. To mitigate disruptions, CSS classes added to the root of the provided HTML are duplicated to the wrapper element, ensuring minimal impact on existing styling implementations.
We expect very few applications will be impacted by this change.
The updated React interop for Tab and AccordionPane components and the generated DOM structure include additional div elements, wrapping both the header and pane body compared to the previous version.
Ready to See What the Latest Release Has to Offer? Download Wijmo Today!