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

Fix RN batching and effect behavior #1444

Merged
merged 8 commits into from
Nov 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 29 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const defaults = {
coverageDirectory: './coverage/',
collectCoverage: true,
testURL: 'http://localhost'
}

const testFolderPath = folderName => `<rootDir>/test/${folderName}/**/*.js`

const NORMAL_TEST_FOLDERS = ['components', 'hooks', 'integration', 'utils']

const standardConfig = {
...defaults,
displayName: 'ReactDOM',
testMatch: NORMAL_TEST_FOLDERS.map(testFolderPath)
}

const rnConfig = {
...defaults,
displayName: 'React Native',
testMatch: [testFolderPath('react-native')],
preset: 'react-native',
transform: {
'^.+\\.js$': '<rootDir>/node_modules/react-native/jest/preprocessor.js'
}
}

module.exports = {
projects: [standardConfig, rnConfig]
}
3,190 changes: 3,139 additions & 51 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 4 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-redux",
"version": "7.1.1",
"version": "7.1.2-alpha.0",
"description": "Official React bindings for Redux",
"keywords": [
"react",
Expand Down Expand Up @@ -65,8 +65,10 @@
"@babel/plugin-transform-runtime": "^7.5.5",
"@babel/preset-env": "^7.5.5",
"@testing-library/jest-dom": "^4.1.0",
"@testing-library/jest-native": "^3.0.2",
"@testing-library/react": "^8.0.8",
"@testing-library/react-hooks": "^1.1.0",
"@testing-library/react-native": "^4.2.0",
"babel-eslint": "^10.0.3",
"babel-jest": "^24.9.0",
"codecov": "^3.5.0",
Expand All @@ -83,6 +85,7 @@
"prettier": "^1.18.2",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-native": "^0.61.4",
"react-test-renderer": "^16.8.6",
"redux": "^4.0.4",
"rimraf": "^3.0.0",
Expand All @@ -97,10 +100,5 @@
"transform": [
"loose-envify"
]
},
"jest": {
"coverageDirectory": "./coverage/",
"collectCoverage": true,
"testURL": "http://localhost"
}
}
22 changes: 2 additions & 20 deletions src/components/connectAdvanced.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
import hoistStatics from 'hoist-non-react-statics'
import invariant from 'invariant'
import React, {
useContext,
useMemo,
useEffect,
useLayoutEffect,
useRef,
useReducer
} from 'react'
import React, { useContext, useMemo, useRef, useReducer } from 'react'
import { isValidElementType, isContextConsumer } from 'react-is'
import Subscription from '../utils/Subscription'
import { useIsomorphicLayoutEffect } from '../utils/useIsomorphicLayoutEffect'

import { ReactReduxContext } from './Context'

Expand All @@ -32,18 +26,6 @@ function storeStateUpdatesReducer(state, action) {

const initStateUpdates = () => [null, 0]

// React currently throws a warning when using useLayoutEffect on the server.
// To get around it, we can conditionally useEffect on the server (no-op) and
// useLayoutEffect in the browser. We need useLayoutEffect because we want
// `connect` to perform sync updates to a ref to save the latest props after
// a render is actually committed to the DOM.
const useIsomorphicLayoutEffect =
typeof window !== 'undefined' &&
typeof window.document !== 'undefined' &&
typeof window.document.createElement !== 'undefined'
? useLayoutEffect
: useEffect

export default function connectAdvanced(
/*
selectorFactory is a func that is responsible for returning the selector function used to
Expand Down
25 changes: 2 additions & 23 deletions src/hooks/useSelector.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,10 @@
import {
useReducer,
useRef,
useEffect,
useMemo,
useLayoutEffect,
useContext
} from 'react'
import { useReducer, useRef, useMemo, useContext } from 'react'
import invariant from 'invariant'
import { useReduxContext as useDefaultReduxContext } from './useReduxContext'
import Subscription from '../utils/Subscription'
import { useIsomorphicLayoutEffect } from '../utils/useIsomorphicLayoutEffect'
import { ReactReduxContext } from '../components/Context'

// React currently throws a warning when using useLayoutEffect on the server.
// To get around it, we can conditionally useEffect on the server (no-op) and
// useLayoutEffect in the browser. We need useLayoutEffect to ensure the store
// subscription callback always has the selector from the latest render commit
// available, otherwise a store update may happen between render and the effect,
// which may cause missed updates; we also must ensure the store subscription
// is created synchronously, otherwise a store update may occur before the
// subscription is created and an inconsistent state may be observed
const useIsomorphicLayoutEffect =
typeof window !== 'undefined' &&
typeof window.document !== 'undefined' &&
typeof window.document.createElement !== 'undefined'
? useLayoutEffect
: useEffect

const refEquality = (a, b) => a === b

function useSelectorWithStoreAndSubscription(
Expand Down
19 changes: 19 additions & 0 deletions src/utils/useIsomorphicLayoutEffect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { useEffect, useLayoutEffect } from 'react'

// React currently throws a warning when using useLayoutEffect on the server.
// To get around it, we can conditionally useEffect on the server (no-op) and
// useLayoutEffect in the browser. We need useLayoutEffect to ensure the store
// subscription callback always has the selector from the latest render commit
// available, otherwise a store update may happen between render and the effect,
// which may cause missed updates; we also must ensure the store subscription
// is created synchronously, otherwise a store update may occur before the
// subscription is created and an inconsistent state may be observed

const isHopefullyDomEnvironment =
typeof window !== 'undefined' &&
typeof window.document !== 'undefined' &&
typeof window.document.createElement !== 'undefined'

export const useIsomorphicLayoutEffect = isHopefullyDomEnvironment
? useLayoutEffect
: useEffect
5 changes: 5 additions & 0 deletions src/utils/useIsomorphicLayoutEffect.native.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { useLayoutEffect } from 'react'

// Under React Native, we know that we always want to use useLayoutEffect

export const useIsomorphicLayoutEffect = useLayoutEffect