Debugging React #31 Minified Error on Android Vivo X7: Symbol Polyfill and Build‑Order Issues
A React component that works locally threw a minified "#31" error only on Vivo X7 Android 5.1 devices because a Symbol polyfill loaded after React caused a mismatch in REACT_ELEMENT_TYPE, and adjusting the build order or JSX runtime resolved the illegal return‑value bug.
The author packaged a React component that passed local tests but triggered a minified React error #31 when deployed to a low‑traffic page accessed on a Vivo X7 Android 5.1 device.
The error message (shown in the monitoring screenshot) indicated that the component's render function returned an illegal value: an object that was not a valid JSX element.
React accepts several return types from render : string , number , array (treated as React.Fragment ), and object that must be a JSX element such as <p>Hello</p> . Returning a plain object like {} triggers the #31 error.
function App() {
return {};
}Investigation revealed that the error object contained the fields $$typeof, type, key, ref, props, _owner , meaning React recognized it as a JSX element but the internal $typeof did not match the expected REACT_ELEMENT_TYPE .
The root cause was the lack of native Symbol support in the Android webview. The project used core‑js to polyfill Symbol , but the polyfill was loaded after the initial react bundle. Consequently, react defined REACT_ELEMENT_TYPE as the numeric constant 0xeac7 , while react‑dom later saw the polyfilled Symbol.for('react.element') and used that as its REACT_ELEMENT_TYPE . The mismatch ( 0xeac7 !== Symbol.for('react.element') ) made React treat the returned object as illegal.
The build order determines which definition wins. If the bundle order is React → core‑js → ReactDOM , the mismatch occurs; if core‑js → React → ReactDOM , both packages share the same polyfilled Symbol and the error disappears.
Fixes include ensuring the Symbol polyfill loads before React (adjusting the bundler configuration), or using the newer JSX runtime ( react/jsx‑runtime ) where createElement is split out, thereby avoiding the mismatch. Updating Babel to downgrade JSX compilation to React.createElement also resolves the issue.
In summary, the bug was not caused by a single component but by the interaction of React, Babel, and a Symbol polyfill in a low‑end Android environment, highlighting the importance of monitoring and understanding build‑order side effects.
ByteFE
Cutting‑edge tech, article sharing, and practical insights from the ByteDance frontend team.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.