Skip to content
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

correct typos #22294

Merged
merged 2 commits into from
Sep 21, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
resolve conflicts
  • Loading branch information
Bowen Li committed Sep 21, 2021
commit 4a1cf5eff7d29824135a1feccd974088a3c5b5ce
316 changes: 0 additions & 316 deletions packages/react-reconciler/src/ReactFiberHooks.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -935,322 +935,6 @@ function rerenderReducer<S, I, A>(
return [newState, dispatch];
}

type MutableSourceMemoizedState<Source, Snapshot> = {|
refs: {
getSnapshot: MutableSourceGetSnapshotFn<Source, Snapshot>,
setSnapshot: Snapshot => void,
},
source: MutableSource<any>,
subscribe: MutableSourceSubscribeFn<Source, Snapshot>,
|};

function readFromUnsubscribedMutableSource<Source, Snapshot>(
root: FiberRoot,
source: MutableSource<Source>,
getSnapshot: MutableSourceGetSnapshotFn<Source, Snapshot>,
): Snapshot {
if (__DEV__) {
warnAboutMultipleRenderersDEV(source);
}

const getVersion = source._getVersion;
const version = getVersion(source._source);

// Is it safe for this component to read from this source during the current render?
let isSafeToReadFromSource = false;

// Check the version first.
// If this render has already been started with a specific version,
// we can use it alone to determine if we can safely read from the source.
const currentRenderVersion = getWorkInProgressVersion(source);
if (currentRenderVersion !== null) {
// It's safe to read if the store hasn't been mutated since the last time
// we read something.
isSafeToReadFromSource = currentRenderVersion === version;
} else {
// If there's no version, then this is the first time we've read from the
// source during the current render pass, so we need to do a bit more work.
// What we need to determine is if there are any hooks that already
// subscribed to the source, and if so, whether there are any pending
// mutations that haven't been synchronized yet.
//
// If there are no pending mutations, then `root.mutableReadLanes` will be
// empty, and we know we can safely read.
//
// If there *are* pending mutations, we may still be able to safely read
// if the currently rendering lanes are inclusive of the pending mutation
// lanes, since that guarantees that the value we're about to read from
// the source is consistent with the values that we read during the most
// recent mutation.
isSafeToReadFromSource = isSubsetOfLanes(
renderLanes,
root.mutableReadLanes,
);

if (isSafeToReadFromSource) {
// If it's safe to read from this source during the current render,
// store the version in case other components read from it.
// A changed version number will let those components know to throw and restart the render.
setWorkInProgressVersion(source, version);
}
}

if (isSafeToReadFromSource) {
const snapshot = getSnapshot(source._source);
if (__DEV__) {
if (typeof snapshot === 'function') {
console.error(
'Mutable source should not return a function as the snapshot value. ' +
'Functions may close over mutable values and cause tearing.',
);
}
}
return snapshot;
} else {
// This handles the special case of a mutable source being shared between renderers.
// In that case, if the source is mutated between the first and second renderer,
// The second renderer don't know that it needs to reset the WIP version during unwind,
// (because the hook only marks sources as dirty if it's written to their WIP version).
// That would cause this tear check to throw again and eventually be visible to the user.
// We can avoid this infinite loop by explicitly marking the source as dirty.
//
// This can lead to tearing in the first renderer when it resumes,
// but there's nothing we can do about that (short of throwing here and refusing to continue the render).
markSourceAsDirty(source);

// Intentionally throw an error to force React to retry synchronously. During
// the synchronous retry, it will block interleaved mutations, so we should
// get a consistent read. Therefore, the following error should never be
// visible to the user.
//
// If it were to become visible to the user, it suggests one of two things:
// a bug in React, or (more likely), a mutation during the render phase that
// caused the second re-render attempt to be different from the first.
//
// We know it's the second case if the logs are currently disabled. So in
// dev, we can present a more accurate error message.
if (__DEV__) {
// eslint-disable-next-line react-internal/no-production-logging
if (getIsStrictModeForDevtools()) {
// If getIsStrictModeForDevtools is true, this is the dev-only double render
// This is only reachable if there was a mutation during render. Show a helpful
// error message.
//
// Something interesting to note: because we only double render in
// development, this error will never happen during production. This is
// actually true of all errors that occur during a double render,
// because if the first render had thrown, we would have exited the
// begin phase without double rendering. We should consider suppressing
// any error from a double render (with a warning) to more closely match
// the production behavior.
const componentName = getComponentNameFromFiber(
currentlyRenderingFiber,
);
invariant(
false,
'A mutable source was mutated while the %s component was rendering. ' +
'This is not supported. Move any mutations into event handlers ' +
'or effects.',
componentName,
);
}
}

// We expect this error not to be thrown during the synchronous retry,
// because we blocked interleaved mutations.
invariant(
false,
'Cannot read from mutable source during the current render without tearing. This may be a bug in React. Please file an issue.',
);
}
}

function useMutableSource<Source, Snapshot>(
hook: Hook,
source: MutableSource<Source>,
getSnapshot: MutableSourceGetSnapshotFn<Source, Snapshot>,
subscribe: MutableSourceSubscribeFn<Source, Snapshot>,
): Snapshot {
const root = ((getWorkInProgressRoot(): any): FiberRoot);
invariant(
root !== null,
'Expected a work-in-progress root. This is a bug in React. Please file an issue.',
);

const getVersion = source._getVersion;
const version = getVersion(source._source);

const dispatcher = ReactCurrentDispatcher.current;

// eslint-disable-next-line prefer-const
let [currentSnapshot, setSnapshot] = dispatcher.useState(() =>
readFromUnsubscribedMutableSource(root, source, getSnapshot),
);
let snapshot = currentSnapshot;

// Grab a handle to the state hook as well.
// We use it to clear the pending update queue if we have a new source.
const stateHook = ((workInProgressHook: any): Hook);

const memoizedState = ((hook.memoizedState: any): MutableSourceMemoizedState<
Source,
Snapshot,
>);
const refs = memoizedState.refs;
const prevGetSnapshot = refs.getSnapshot;
const prevSource = memoizedState.source;
const prevSubscribe = memoizedState.subscribe;

const fiber = currentlyRenderingFiber;

hook.memoizedState = ({
refs,
source,
subscribe,
}: MutableSourceMemoizedState<Source, Snapshot>);

// Sync the values needed by our subscription handler after each commit.
dispatcher.useEffect(() => {
refs.getSnapshot = getSnapshot;

// Normally the dispatch function for a state hook never changes,
// but this hook recreates the queue in certain cases to avoid updates from stale sources.
// handleChange() below needs to reference the dispatch function without re-subscribing,
// so we use a ref to ensure that it always has the latest version.
refs.setSnapshot = setSnapshot;

// Check for a possible change between when we last rendered now.
const maybeNewVersion = getVersion(source._source);
if (!is(version, maybeNewVersion)) {
const maybeNewSnapshot = getSnapshot(source._source);
if (__DEV__) {
if (typeof maybeNewSnapshot === 'function') {
console.error(
'Mutable source should not return a function as the snapshot value. ' +
'Functions may close over mutable values and cause tearing.',
);
}
}

if (!is(snapshot, maybeNewSnapshot)) {
setSnapshot(maybeNewSnapshot);

const lane = requestUpdateLane(fiber);
markRootMutableRead(root, lane);
}
// If the source mutated between render and now,
// there may be state updates already scheduled from the old source.
// Entangle the updates so that they render in the same batch.
markRootEntangled(root, root.mutableReadLanes);
}
}, [getSnapshot, source, subscribe]);

// If we got a new source or subscribe function, re-subscribe in a passive effect.
dispatcher.useEffect(() => {
const handleChange = () => {
const latestGetSnapshot = refs.getSnapshot;
const latestSetSnapshot = refs.setSnapshot;

try {
latestSetSnapshot(latestGetSnapshot(source._source));

// Record a pending mutable source update with the same expiration time.
const lane = requestUpdateLane(fiber);

markRootMutableRead(root, lane);
} catch (error) {
// A selector might throw after a source mutation.
// e.g. it might try to read from a part of the store that no longer exists.
// In this case we should still schedule an update with React.
// Worst case the selector will throw again and then an error boundary will handle it.
latestSetSnapshot(
(() => {
throw error;
}: any),
);
}
};

const unsubscribe = subscribe(source._source, handleChange);
if (__DEV__) {
if (typeof unsubscribe !== 'function') {
console.error(
'Mutable source subscribe function must return an unsubscribe function.',
);
}
}

return unsubscribe;
}, [source, subscribe]);

// If any of the inputs to useMutableSource change, reading is potentially unsafe.
//
// If either the source or the subscription have changed we can't can't trust the update queue.
// Maybe the source changed in a way that the old subscription ignored but the new one depends on.
//
// If the getSnapshot function changed, we also shouldn't rely on the update queue.
// It's possible that the underlying source was mutated between the when the last "change" event fired,
// and when the current render (with the new getSnapshot function) is processed.
//
// In both cases, we need to throw away pending updates (since they are no longer relevant)
// and treat reading from the source as we do in the mount case.
if (
!is(prevGetSnapshot, getSnapshot) ||
!is(prevSource, source) ||
!is(prevSubscribe, subscribe)
) {
// Create a new queue and setState method,
// So if there are interleaved updates, they get pushed to the older queue.
// When this becomes current, the previous queue and dispatch method will be discarded,
// including any interleaving updates that occur.
const newQueue: UpdateQueue<Snapshot, BasicStateAction<Snapshot>> = {
pending: null,
interleaved: null,
lanes: NoLanes,
dispatch: null,
lastRenderedReducer: basicStateReducer,
lastRenderedState: snapshot,
};
newQueue.dispatch = setSnapshot = (dispatchAction.bind(
null,
currentlyRenderingFiber,
newQueue,
): any);
stateHook.queue = newQueue;
stateHook.baseQueue = null;
snapshot = readFromUnsubscribedMutableSource(root, source, getSnapshot);
stateHook.memoizedState = stateHook.baseState = snapshot;
}

return snapshot;
}

function mountMutableSource<Source, Snapshot>(
source: MutableSource<Source>,
getSnapshot: MutableSourceGetSnapshotFn<Source, Snapshot>,
subscribe: MutableSourceSubscribeFn<Source, Snapshot>,
): Snapshot {
const hook = mountWorkInProgressHook();
hook.memoizedState = ({
refs: {
getSnapshot,
setSnapshot: (null: any),
},
source,
subscribe,
}: MutableSourceMemoizedState<Source, Snapshot>);
return useMutableSource(hook, source, getSnapshot, subscribe);
}

function updateMutableSource<Source, Snapshot>(
source: MutableSource<Source>,
getSnapshot: MutableSourceGetSnapshotFn<Source, Snapshot>,
subscribe: MutableSourceSubscribeFn<Source, Snapshot>,
): Snapshot {
const hook = updateWorkInProgressHook();
return useMutableSource(hook, source, getSnapshot, subscribe);
}

function mountSyncExternalStore<T>(
subscribe: (() => void) => () => void,
getSnapshot: () => T,
Expand Down
Loading
You are viewing a condensed version of this merge commit. You can view the full changes here.