Skip to content

Skeleton for store #33215

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions packages/react-reconciler/src/ReactFiberHooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {
Thenable,
RejectedThenable,
Awaited,
ReactStore,
} from 'shared/ReactTypes';
import type {
Fiber,
Expand Down Expand Up @@ -44,10 +45,12 @@ import {
enableNoCloningMemoCache,
enableViewTransition,
enableGestureTransition,
enableStore,
} from 'shared/ReactFeatureFlags';
import {
REACT_CONTEXT_TYPE,
REACT_MEMO_CACHE_SENTINEL,
REACT_STORE_TYPE,
} from 'shared/ReactSymbols';

import {
Expand Down Expand Up @@ -1145,6 +1148,10 @@ function useThenable<T>(thenable: Thenable<T>): T {
return result;
}

function useStore<T>(store: ReactStore<T>): T {
return store._current;
}

function use<T>(usable: Usable<T>): T {
if (usable !== null && typeof usable === 'object') {
// $FlowFixMe[method-unbinding]
Expand All @@ -1156,6 +1163,12 @@ function use<T>(usable: Usable<T>): T {
const context: ReactContext<T> = (usable: any);
return readContext(context);
}
if (enableStore) {
if (usable.$$typeof === REACT_STORE_TYPE) {
const store: ReactStore<T> = (usable: any);
return useStore(store);
}
}
}

// eslint-disable-next-line react-internal/safe-string-coercion
Expand Down
51 changes: 51 additions & 0 deletions packages/react-reconciler/src/__tests__/ReactUseStore-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails react-core
*/
'use strict';

let React;
let ReactNoop;
let Scheduler;
let act;
let use;
let assertLog;
let createStore;

describe('ReactUse', () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ReactUseStore?

beforeEach(() => {
jest.resetModules();

React = require('react');
ReactNoop = require('react-noop-renderer');
Scheduler = require('scheduler');
act = require('internal-test-utils').act;
use = React.use;
createStore = React.unstable_createStore;

const InternalTestUtils = require('internal-test-utils');
assertLog = InternalTestUtils.assertLog;
});

// @gate enableStore
it('should read the current value', async () => {
const store = createStore(1);

function App() {
const value = use(store);
Scheduler.log(value);
return <span>{value}</span>;
}

const root = ReactNoop.createRoot();
await act(() => {
root.render(<App />);
});
assertLog([1]);
expect(root).toMatchRenderedOutput(<span>1</span>);
});
});
13 changes: 11 additions & 2 deletions packages/react/index.fb.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
* @flow
*/

import {captureOwnerStack as captureOwnerStackImpl} from './src/ReactClient';
import {
captureOwnerStack as captureOwnerStackImpl,
unstable_createStore as createStore,
} from './src/ReactClient';
import {enableStore} from 'shared/ReactFeatureFlags';

export {
__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,
Expand Down Expand Up @@ -76,4 +80,9 @@ if (__DEV__) {
captureOwnerStack = captureOwnerStackImpl;
}

export {captureOwnerStack};
let unstable_createStore: typeof createStore;
if (enableStore) {
unstable_createStore = createStore;
}

export {captureOwnerStack, unstable_createStore};
3 changes: 3 additions & 0 deletions packages/react/src/ReactClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
isValidElement,
} from './jsx/ReactJSXElement';
import {createContext} from './ReactContext';
import {createStore} from './ReactStore';
import {lazy} from './ReactLazy';
import {forwardRef} from './ReactForwardRef';
import {memo} from './ReactMemo';
Expand Down Expand Up @@ -128,6 +129,8 @@ export {
addTransitionType as unstable_addTransitionType,
// enableGestureTransition
startGestureTransition as unstable_startGestureTransition,
// enableStore
createStore as unstable_createStore,
// DEV-only
useId,
act,
Expand Down
31 changes: 31 additions & 0 deletions packages/react/src/ReactStore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

import type {ReactStore} from 'shared/ReactTypes';
import {REACT_STORE_TYPE} from 'shared/ReactSymbols';
import {enableStore} from 'shared/ReactFeatureFlags';

export function createStore<T>(
defaultValue: T,
reducer?: (T, mixed) => T,
): ReactStore<T> {
Comment on lines +14 to +17
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be able have strong types for reducer actions while keeping both reducers and actions optional.

Suggested change
export function createStore<T>(
defaultValue: T,
reducer?: (T, mixed) => T,
): ReactStore<T> {
export function createStore<Value, Action = empty>(
defaultValue: T,
reducer?: (value: T, action?: Action) => T,
): ReactStore<T, Action> {

if (!enableStore) {
throw new Error('Not implemented.');
}

const store: ReactStore<T> = {
$$typeof: REACT_STORE_TYPE,

_current: defaultValue,
_sync: defaultValue,
_transition: defaultValue,
};
Comment on lines +22 to +28
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const store: ReactStore<T> = {
$$typeof: REACT_STORE_TYPE,
_current: defaultValue,
_sync: defaultValue,
_transition: defaultValue,
};
const store: ReactStore<Value, mixed> = {
$$typeof: REACT_STORE_TYPE,
_current: defaultValue,
_sync: defaultValue,
_transition: defaultValue,
};


return store;
}
2 changes: 2 additions & 0 deletions packages/shared/ReactFeatureFlags.js
Original file line number Diff line number Diff line change
Expand Up @@ -270,3 +270,5 @@ export const enableUpdaterTracking = __PROFILE__;
export const enableDO_NOT_USE_disableStrictPassiveEffect = false;

export const ownerStackLimit = 1e4;

export const enableStore = false;
2 changes: 2 additions & 0 deletions packages/shared/ReactSymbols.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ export const REACT_VIEW_TRANSITION_TYPE: symbol = Symbol.for(
'react.view_transition',
);

export const REACT_STORE_TYPE: symbol = Symbol.for('react.store');

const MAYBE_ITERATOR_SYMBOL = Symbol.iterator;
const FAUX_ITERATOR_SYMBOL = '@@iterator';

Expand Down
9 changes: 8 additions & 1 deletion packages/shared/ReactTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@ export type ReactContext<T> = {
displayName?: string,
};

export type ReactStore<T> = {
$$typeof: symbol,
_current: T,
_sync: T,
_transition: T,
};

Comment on lines +67 to +73
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if the Action type makes sense anymore for this PR.
But the + should make the value co-variant, (making it possible to Store<Dog> where a Store<Animal> is needed)

While the - makes Action contra-variant (allowing you to pass a Dog when an Animal is accepted as/within an Action.)

Suggested change
export type ReactStore<T> = {
$$typeof: symbol,
_current: T,
_sync: T,
_transition: T,
};
export type ReactStore<+Value, -Action = empty> = {
$$typeof: symbol,
_current: Value,
_sync: Value,
_transition: Value,
};

export type ReactPortal = {
$$typeof: symbol | number,
key: null | string,
Expand Down Expand Up @@ -141,7 +148,7 @@ export type StartTransitionOptions = {
name?: string,
};

export type Usable<T> = Thenable<T> | ReactContext<T>;
export type Usable<T> = Thenable<T> | ReactContext<T> | ReactStore<T>;

export type ReactCustomFormAction = {
name?: string,
Expand Down
1 change: 1 addition & 0 deletions packages/shared/forks/ReactFeatureFlags.native-fb.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export const enableSrcObject = false;
export const enableHydrationChangeEvent = true;
export const enableDefaultTransitionIndicator = false;
export const ownerStackLimit = 1e4;
export const enableStore = false;

// Flow magic to verify the exports of this file match the original version.
((((null: any): ExportsType): FeatureFlagsType): ExportsType);
1 change: 1 addition & 0 deletions packages/shared/forks/ReactFeatureFlags.native-oss.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export const enableProfilerTimer = __PROFILE__;
export const enableProfilerCommitHooks = __PROFILE__;
export const enableProfilerNestedUpdatePhase = __PROFILE__;
export const enableUpdaterTracking = __PROFILE__;
export const enableStore = false;

// Flow magic to verify the exports of this file match the original version.
((((null: any): ExportsType): FeatureFlagsType): ExportsType);
1 change: 1 addition & 0 deletions packages/shared/forks/ReactFeatureFlags.test-renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export const enableReactTestRendererWarning = true;
export const disableDefaultPropsExceptForClasses = true;

export const enableObjectFiber = false;
export const enableStore = false;

// Flow magic to verify the exports of this file match the original version.
((((null: any): ExportsType): FeatureFlagsType): ExportsType);
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export const enableHydrationChangeEvent = false;
export const enableDefaultTransitionIndicator = false;
export const enableFragmentRefs = false;
export const ownerStackLimit = 1e4;
export const enableStore = false;

// Flow magic to verify the exports of this file match the original version.
((((null: any): ExportsType): FeatureFlagsType): ExportsType);
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export const enableDefaultTransitionIndicator = false;

export const enableFragmentRefs = false;
export const ownerStackLimit = 1e4;
export const enableStore = false;

// Flow magic to verify the exports of this file match the original version.
((((null: any): ExportsType): FeatureFlagsType): ExportsType);
1 change: 1 addition & 0 deletions packages/shared/forks/ReactFeatureFlags.www-dynamic.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export const enableViewTransition = __VARIANT__;
export const enableComponentPerformanceTrack = __VARIANT__;
export const enableScrollEndPolyfill = __VARIANT__;
export const enableFragmentRefs = __VARIANT__;
export const enableStore = __VARIANT__;

// TODO: These flags are hard-coded to the default values used in open source.
// Update the tests so that they pass in either mode, then set these
Expand Down
1 change: 1 addition & 0 deletions packages/shared/forks/ReactFeatureFlags.www.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export const {
enableComponentPerformanceTrack,
enableScrollEndPolyfill,
enableFragmentRefs,
enableStore,
} = dynamicFeatureFlags;

// On WWW, __EXPERIMENTAL__ is used for a new modern build.
Expand Down
Loading