diff --git a/.circleci/config.yml b/.circleci/config.yml index aba97b8..9cc7317 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,9 +6,12 @@ version: 2 jobs: build: docker: - - image: circleci/node:12.14 + - image: cimg/node:14.21 steps: + - run: + name: Node version + command: node -v - checkout - restore_cache: keys: diff --git a/src/react/connectAs.tsx b/src/react/connectAs.tsx index 79d62e1..249136b 100644 --- a/src/react/connectAs.tsx +++ b/src/react/connectAs.tsx @@ -18,18 +18,21 @@ export function connectAs< stores: { [K in keyof Stores]: ReturnType } - subscriptions: Subscription[] } return class extends React.Component, State> { static displayName = `withStore(${getDisplayName(Component)})` + subscriptions: Subscription[] = [] state = { stores: mapValues( stores, _ => _.getCurrentSnapshot() as ReturnType - ), - subscriptions: keys(stores).map(k => + ) + } + + componentDidMount(): void { + this.subscriptions = keys(stores).map(k => stores[k].onAll().subscribe(({ previousValue, value }) => { if (equals(previousValue, value)) { return false @@ -44,7 +47,8 @@ export function connectAs< } componentWillUnmount() { - this.state.subscriptions.forEach(_ => _.unsubscribe()) + this.subscriptions.forEach(_ => _.unsubscribe()) + this.subscriptions = [] } shouldComponentUpdate(props: Diff, state: State) { diff --git a/src/react/createConnectedStore.tsx b/src/react/createConnectedStore.tsx index 25b926c..4d65577 100644 --- a/src/react/createConnectedStore.tsx +++ b/src/react/createConnectedStore.tsx @@ -30,7 +30,7 @@ export function createConnectedStore( ContainerProps, ContainerState > { - subscription: Subscription + subscription: Subscription | null = null storeDefinition: StoreDefinition constructor(props: ContainerProps) { super(props) @@ -45,23 +45,30 @@ export function createConnectedStore( fx(this.storeDefinition) } + this.subscription = this._createSubscription() this.state = { storeSnapshot: this.storeDefinition.getCurrentSnapshot() } + } - this.subscription = this.storeDefinition.onAll().subscribe(() => + _createSubscription = () => + this.storeDefinition.onAll().subscribe(() => this.setState({ storeSnapshot: this.storeDefinition.getCurrentSnapshot() }) ) + + componentDidMount(): void { + if (this.subscription == null) { + this.subscription = this._createSubscription() + } } componentWillUnmount() { - this.subscription.unsubscribe() - // Let the state get GC'd. - // TODO: Find a more elegant way to do this. - ;(this.storeDefinition as any).storeSnapshot = null - ;(this as any).storeDefinition = null + if (this.subscription != null) { + this.subscription.unsubscribe() + this.subscription = null + } } render() { diff --git a/src/react/createConnectedStoreAs.tsx b/src/react/createConnectedStoreAs.tsx index 2005410..ece18d9 100644 --- a/src/react/createConnectedStoreAs.tsx +++ b/src/react/createConnectedStoreAs.tsx @@ -43,15 +43,16 @@ export function createConnectedStoreAs< let Context = React.createContext({ __MISSING_PROVIDER__: true } as any) type ContainerState = { - storeDefinitions: { [K in keyof States]: StoreDefinition | null } storeSnapshots: { [K in keyof States]: StoreSnapshot | null } - subscriptions: { [K in keyof States]: Subscription } } class Container extends React.Component< ContainerPropsAs, ContainerState > { + storeDefinitions: { [K in keyof States]: StoreDefinition } + subscriptions: { [K in keyof States]: Subscription } | null + constructor(props: ContainerPropsAs) { super(props) @@ -65,37 +66,36 @@ export function createConnectedStoreAs< fx(stores as any) // TODO } + this.storeDefinitions = stores as any + this.subscriptions = this._createSubscriptions() this.state = { - storeDefinitions: stores as any, // TODO // TODO - storeSnapshots: mapValues(stores, _ => _.getCurrentSnapshot()) as any, - subscriptions: mapValues(stores, (_, k) => - _.onAll().subscribe(() => - this.setState(state => ({ - storeSnapshots: Object.assign({}, state.storeSnapshots, { - [k]: _.getCurrentSnapshot() - }) - })) - ) + storeSnapshots: mapValues(stores, _ => _.getCurrentSnapshot()) as any + } + } + + _createSubscriptions = () => + mapValues(this.storeDefinitions, (_, k) => + _.onAll().subscribe(() => + this.setState(state => ({ + storeSnapshots: Object.assign({}, state.storeSnapshots, { + [k]: _.getCurrentSnapshot() + }) + })) ) + ) as any + + componentDidMount(): void { + if (this.subscriptions == null) { + this.subscriptions = this._createSubscriptions() } } componentWillUnmount() { - mapValues(this.state.subscriptions, _ => _.unsubscribe()) - // Let the state get GC'd. - // TODO: Find a more elegant way to do this. - if (this.state.storeSnapshots) { + if (this.subscriptions != null) { + mapValues(this.subscriptions, _ => _.unsubscribe()) + this.subscriptions = null } - mapValues(this.state.storeSnapshots, _ => ((_ as any).state = null)) - mapValues( - this.state.storeSnapshots, - _ => ((_ as any).storeDefinition = null) - ) - mapValues( - this.state.storeDefinitions, - _ => ((_ as any).storeSnapshot = null) - ) } render() {