Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Use React.createContext() by @markerikson#995

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Closed
markerikson wants to merge13 commits intomasterfromv6-experiment-2
Closed
Show file tree
Hide file tree
Changes from1 commit
Commits
Show all changes
13 commits
Select commitHold shift + click to select a range
4e654e2
Copy changes from original v6 experiment branch
markeriksonAug 11, 2018
31fc951
Split Connect into two wrapper components
markeriksonAug 11, 2018
0374936
Clean up and finish reworking Connect and Provider
markeriksonAug 12, 2018
a64103c
Don't call setState in Provider if unmounting
markeriksonAug 12, 2018
ed7a910
Rework tests based on changes to connect and Provider
markeriksonAug 14, 2018
3cf4a0f
Fix lint errors
markeriksonAug 18, 2018
6bfedb1
Add react-is
markeriksonAug 18, 2018
5df5a0f
Add missing functionality for connect and Provider
markeriksonAug 18, 2018
1fe01d0
Borrow a bunch of tests from Greg's 1000 branch
markeriksonAug 18, 2018
0d0360f
Include `react-is` in build output
markeriksonAug 24, 2018
577efb0
Try using a functional component instead of forwardRef
markeriksonAug 24, 2018
7f72494
Temporarily undo all connect wrappings
markeriksonAug 24, 2018
9210282
Make forwardRef optional
markeriksonSep 13, 2018
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
PrevPrevious commit
NextNext commit
Clean up and finish reworking Connect and Provider
  • Loading branch information
@markerikson
markerikson committedAug 14, 2018
commit03749361e63bbc700e2068359056f941c55a3190
1 change: 1 addition & 0 deletionssrc/components/Provider.js
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -77,6 +77,7 @@ export function createProvider(storeKey = 'store') {
if (state.store !== props.store) {
warnAboutReceivingStore()
Copy link
Contributor

Choose a reason for hiding this comment

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

this is not required with the new context any longer, we can change the context value and children are updated

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

But, the selector initialization and binding of action creators happens when the connected children are instantiated - we don't try to re-bind those if the store is swapped out.

Copy link
Contributor

Choose a reason for hiding this comment

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

We can rebind in cDU, 1 line change

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

Not something I'm particularly worried about right now. There may besomeone out there who has a need to swap out their store, but it's not something we've ever supported and I don't see a need to try to support it for the time being.

}
return null;
}
}

Expand Down
149 changes: 28 additions & 121 deletionssrc/components/connectAdvanced.js
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,11 @@
import hoistStatics from 'hoist-non-react-statics'
import invariant from 'invariant'
import React, { Component, createElement } from 'react'
import React, { Component,PureComponent,createElement } from 'react'

import {ReactReduxContext} from "./context";
import shallowEqual from '../utils/shallowEqual'

let hotReloadingVersion = 0
const dummyState = {}
function noop() {}
function makeSelectorStateful(sourceSelector) {
// wrap the selector in an object that tracks its results between runs.
const selector = {
run: function runComponentSelector(props, storeState) {
try {
const nextProps = sourceSelector(storeState, props)
if (nextProps !== selector.props || selector.error) {
selector.shouldComponentUpdate = true
selector.props = nextProps
selector.error = null
}
} catch (error) {
selector.shouldComponentUpdate = true
selector.error = error
}
}
}

return selector
}

export default function connectAdvanced(
/*
Expand DownExpand Up@@ -105,12 +83,13 @@ export default function connectAdvanced(
}


const OuterBaseComponent = connectOptions.pure ? PureComponent : Component;

class ConnectInner extends Component {
constructor(props) {
super(props);

this.state = {
storeState : props.storeState,
wrapperProps : props.wrapperProps,
renderCount : 0,
store : props.store,
Expand All@@ -131,7 +110,7 @@ export default function connectAdvanced(

static getChildPropsState(props, state) {
try {
const nextProps = state.childPropsSelector(state.storeState, props.wrapperProps)
const nextProps = state.childPropsSelector(props.storeState, props.wrapperProps)
if (nextProps === state.childProps) return null
return { childProps: nextProps }
} catch (error) {
Expand All@@ -140,20 +119,28 @@ export default function connectAdvanced(
}

static getDerivedStateFromProps(props, state) {
if ((connectOptions.pure && shallowEqual(props.wrapperProps, state.wrapperProps)) || state.error){
const nextChildProps = ConnectInner.getChildPropsState(props, state)

if(nextChildProps === null) {
return null;
}

const nextChildProps = ConnectInner.getChildPropsState(props, state)

return {
...nextChildProps,
wrapperProps : props.wrapperProps,
}
}

shouldComponentUpdate(nextProps, nextState) {
return nextState.childProps !== this.state.childProps;
const childPropsChanged = nextState.childProps !== this.state.childProps;
const storeStateChanged = nextProps.storeState !== this.props.storeState;
const hasError = !!nextState.error;

let wrapperPropsChanged = false;

const shouldUpdate = childPropsChanged || hasError;
return shouldUpdate;

}

render() {
Expand All@@ -165,75 +152,13 @@ export default function connectAdvanced(
}
}

class Connect extendsComponent {
class Connect extendsOuterBaseComponent {
constructor(props) {
super(props)

this.version = version
this.renderCount = 0
this.storeState = null;


//this.setWrappedInstance = this.setWrappedInstance.bind(this)
this.renderInner = this.renderInner.bind(this);

// TODO How do we express the invariant of needing a Provider when it's used in render()?
/*
invariant(this.store,
`Could not find "${storeKey}" in either the context or props of ` +
`"${displayName}". Either wrap the root component in a <Provider>, ` +
`or explicitly pass "${storeKey}" as a prop to "${displayName}".`
)
*/
}

componentDidMount() {
if (!shouldHandleStateChanges) return

// componentWillMount fires during server side rendering, but componentDidMount and
// componentWillUnmount do not. Because of this, trySubscribe happens during ...didMount.
// Otherwise, unsubscription would never take place during SSR, causing a memory leak.
// To handle the case where a child component may have triggered a state change by
// dispatching an action in its componentWillMount, we have to re-run the select and maybe
// re-render.
this.selector.run(this.props, this.storeState);
if (this.selector.shouldComponentUpdate) this.forceUpdate()
}


UNSAFE_componentWillReceiveProps(nextProps) {
// TODO Do we still want/need to implement sCU / cWRP now?
this.selector.run(nextProps, this.storeState);
}

shouldComponentUpdate() {
return this.selector.shouldComponentUpdate
}


componentWillUnmount() {
this.selector.run = noop
this.selector.shouldComponentUpdate = false
}

getWrappedInstance() {
invariant(withRef,
`To access the wrapped instance, you need to specify ` +
`{ withRef: true } in the options argument of the ${methodName}() call.`
)
return this.wrappedInstance
}

setWrappedInstance(ref) {
this.wrappedInstance = ref
}

initSelector(dispatch, storeState) {
const sourceSelector = selectorFactory(dispatch, selectorFactoryOptions)
this.selector = makeSelectorStateful(sourceSelector)
this.selector.run(this.props, storeState);
}

/*
addExtraProps(props) {
if (!withRef && !renderCountProp) return props;

Expand All@@ -247,37 +172,19 @@ export default function connectAdvanced(

return withExtras
}
*/

renderInner(providerValue) {
const {storeState, store} = providerValue;
Copy link
Contributor

Choose a reason for hiding this comment

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

here, I would rather do the calculation of derived props and memoize them, then the inner component need only be the WrappedComponent or a PureComponent that wraps it, something like:

constInner=connectOptions.pure ?classInnerextendsPureComponent{render(){return<WrappedComponent{...this.props}/>}} :WrappedComponent

then renderInner can be something like:

renderInner({ storeState, store}){constprops=this.propsconstderivedProps=this.memoizedProps(storeState,props)return<Inner{...props}{...derivedProps}/>}

Our memoizer can be initialized in the constructor withthis.memoizedProps = this.makeMemoizer()

makeMemoizer(){letlastPropsletlastStateletlastDerivedPropsletcalled=falsereturn(state,props)=>{if(called){constsameProps=connectOptions.pure&&shallowEqual(lastProps,props)constsameState=lastState===stateif(sameProps&&sameState){returnlastDerivedProps}}called=truelastProps=propslastState=stateconstnextProps=this.state.childPropsSelector(state,props)if(shallowEqual(lastDerivedProps,nextProps)){returnlastDerivedProps}lastDerivedProps=nextPropsreturnlastDerivedProps}}

In this way, we don't needgetDerivedStateFromProps at all

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

Let's try that approach as a second PR, for comparison purposes.

cellog reacted with thumbs up emoji

/*
this.storeState = storeState;

if(this.selector) {
this.selector.run(this.props, storeState);
}
else {
this.initSelector(dispatch, storeState);
}

if (this.selector.error) {
// TODO This will unmount the whole tree now that we're throwing in render. Good idea?
// TODO Related: https://github.com/reactjs/react-redux/issues/802
throw this.selector.error
}
else if(this.selector.shouldComponentUpdate) {
//console.log(`Re-rendering component (${displayName})`, this.selector.props);
this.selector.shouldComponentUpdate = false;
this.renderedElement = createElement(WrappedComponent, this.addExtraProps(this.selector.props));
}
else {
//console.log(`Returning existing render result (${displayName})`, this.props)
}

return this.renderedElement;
*/
return <ConnectInner storeState={storeState} store={store} wrapperProps={this.props} />
return (
<ConnectInner
key={this.version}
storeState={storeState}
store={store}
wrapperProps={this.props}
/>
);
}

render() {
Expand All@@ -291,8 +198,8 @@ export default function connectAdvanced(

Connect.WrappedComponent = WrappedComponent
Connect.displayName = displayName

// TODO We're losing the ability to add a store as a prop. Not sure there's anything we can do about that.
//Connect.propTypes = contextTypes

Copy link
Contributor

@cellogcellogAug 12, 2018
edited
Loading

Choose a reason for hiding this comment

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

I still think we can manage this, but by allowing passing a separate context consumer to connect. This way, users can use a custom context provider and just hook into it with their specific components. In other words, we providecreateProviderContext and allow:

const{ SpecialProvider, SpecialConsumer}=createProviderContext()constConnectedNormal=connect(...)(Thing)constConnectedOther=connect(mSP,mDP,merge,{contextConsumer:SpecialConsumer})(Thing)functionSomething(){return(<Providerstore={store}><Providercontext={SpecialProvider}store={store2}><ConnectedNormal><ConnectedOther/></ConnectedNormal></Provider></Provider>)}

This way, we get the full advantage of React's context, but have flexibility.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

We could potentially allow passing aContext.Consumer directly to a connected component as a prop, although we might have to do some fiddling around because this currently tries to pass all the wrapper props down directly to the inner component.

That said, I'm not worried about trying to implement store-as-a-prop or anything like that atm. My main concern is getting the data flow and update behavior working right - we can add everything else after that.

cellog reacted with thumbs up emoji
// TODO With connect no longer managing subscriptions, I _think_ is is all unneeded
/*
Expand Down

[8]ページ先頭

©2009-2025 Movatter.jp