Change:
It seems like react-is v16.6 no longer is compatible with React v16.3 - v16.5, in that React v16.3 - v16.5 will use , but 's export will instead be . It seems like the async mode symbol needs to remain the same in react-is 16.6+, and the function would need to check both the AsyncMode and ConcurrentMode symbols.
(Note that this blocks full enzyme support of react v16.6)
Can you explain more about the case in which it’s breaking?
sure - the enzyme react 16 adapter needs to maintain support for all versions of React 16. It provides a "getDisplayNameOfNode" function, which needs to be able to return the string in React 16.3-16.5 (for ), and the string in React 16.6+ (for ). It does this by comparing the to the or symbol exports from . In the 16.3-specific adapter, this isn't an issue, but it does have to lock the version of to (which is the breaking change part) - it should be able to keep working with react v16.3 and 16.anything. If v16.6+ had an symbol export that matched what React 16.3-16.5 did, then I think this wouldn't be a problem.
If react-is v16.6+ had an AsyncMode symbol export that matched what React 16.3-16.5 did, then I think this wouldn't be a problem. I think that sounds okay, we could do it. PR?
Sure, I'll try my hand at it!
This is our mistake because we should have prefixed isAsyncMode with unstable which the actual api was. Or not included it at all. We’re treating unstable effectively as private and will break between minors. Are you ok with not supporting calls that are unstable_ in the future? Because we’ll break them several times between releases and don’t want to keep them all around in bundles.
Prefix or not, renaming things is still going to break consumers. However, I understand react decides to accept this breakage in minor versions. Either way, the value of is that it abstracts over these kind of changes. "Not including it at all" would have meant I had to hardcode this stuff inside enzyme, which defeats the whole purpose of in the first place. In this case, the bundle size difference is negligible - when this is the case, it seems worth trying to keep back compat, even on things react has decided are effectively private.
The alternative is that we put it behind a feature flag and don’t expose the featur at all. But then it’s much harder for people to try it out for cases where breakages are ok. It’s really hard for people in many environments to use alpha builds.
I understand the dilemma. In this case, the functionality hasn't changed, and I'm not sure why it was renamed - iow i'm not sure how it helps people try it out to have the name change now, when it's still going to have to change again when it gets promoted to being stable. The tradeoffs seem to be somewhere between "difficult for people to try it out", "cause breakage in some amount of downstream tooling", or "incur a tiny additional bundle cost to maintain back compat". It's a tough call, and I'm not asking for a policy change - but in this case, the bundle size increase of seems pretty close to zero (i can't imagine it's more than a handful of bytes), and the benefit is that enzyme (and presumably other downstream tools) will be able to continue using without having to hardcode things.

Discussion:
'use strict'; import { <ins> REACT_ASYNC_MODE_TYPE, </ins> REACT_CONCURRENT_MODE_TYPE, REACT_CONTEXT_TYPE, REACT_ELEMENT_TYPE,

const type = object.type; switch (type) { <ins> case REACT_ASYNC_MODE_TYPE: </ins> case REACT_CONCURRENT_MODE_TYPE: case REACT_FRAGMENT_TYPE: case REACT_PROFILER_TYPE:

return undefined; } <del> // AsyncMode alias is deprecated along with isAsyncMode export const AsyncMode = REACT_CONCURRENT_MODE_TYPE; </del> <ins> // AsyncMode is deprecated along with isAsyncMode export const AsyncMode = REACT_ASYNC_MODE_TYPE; </ins> export const ConcurrentMode = REACT_CONCURRENT_MODE_TYPE; export const ContextConsumer = REACT_CONTEXT_TYPE; export const ContextProvider = REACT_PROVIDER_TYPE;

); } } <del> return isConcurrentMode(object); </del> <ins> return isConcurrentMode(object) || typeOf(object) === REACT_ASYNC_MODE_TYPE; </ins> } export function isConcurrentMode(object: any) { return typeOf(object) === REACT_CONCURRENT_MODE_TYPE;

expect(ReactIs.isValidElementType({type: 'div', props: {}})).toEqual(false); }); <del> it('should identify async mode', () => { </del> <ins> it('should identify concurrent mode', () => { </ins> expect(ReactIs.typeOf(<React.unstable_ConcurrentMode />)).toBe( ReactIs.ConcurrentMode, );

export const REACT_CONTEXT_TYPE = hasSymbol ? Symbol.for('react.context') : 0xeace; <ins> export const REACT_ASYNC_MODE_TYPE = hasSymbol ? Symbol.for('react.async_mode') : 0xeacf; </ins> export const REACT_CONCURRENT_MODE_TYPE = hasSymbol ? Symbol.for('react.concurrent_mode') : 0xeacf;

const originalSymbolFor = global.Symbol.for; global.Symbol.for = null; try { <del> expectToBeUnique(Object.entries(require('shared/ReactSymbols'))); </del> <ins> const entries = Object.entries(require('shared/ReactSymbols')).filter( // REACT_ASYNC_MODE_TYPE and REACT_CONCURRENT_MODE_TYPE have the same numeric value // for legacy backwards compatibility ([key]) => key !== 'REACT_ASYNC_MODE_TYPE', ); expectToBeUnique(entries); </ins> } finally { global.Symbol.for = originalSymbolFor; }