Skip to content
This repository was archived by the owner on Feb 19, 2022. It is now read-only.
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
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ When submitting a bug report, make sure to include a repro. The best way to do
- [`initialState`](#initialstate)
- [`effects`](#effects)
- [`initialize`](#initialize)
- [`finalize`](#finalize)
- [`computed`](#computed)
- [`middleware`](#middleware)
- [`injectState`](#injectstate)
Expand Down Expand Up @@ -909,6 +910,11 @@ Each state container can define a special effect called `initialize`. This effe

Note: this effect will NOT be passed down to a component's children.

##### `finalize`

`initialize` counterpart, invoked immediately before a component is unmounted and destroyed (browser only).

Note: this effect will NOT be passed down to a component's children.

#### `computed`

Expand Down
51 changes: 51 additions & 0 deletions spec/integration/finalize.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from "react";
import { mount } from "enzyme";

import { provideState, injectState, softUpdate } from "freactal";


const Child = () => <div />;
const ChildWithState = injectState(Child);
const wrapChildWithState = provideState({});
const StatefulChild = wrapChildWithState(ChildWithState);

const Parent = ({ state: { renderChildren, finalized }, children }) => (
<div>
<div className="parent-finalized">{ finalized ? "true" : "false" }</div>
{renderChildren ? children : null}
</div>
);
const ParentWithState = injectState(Parent);
const wrapParentWithState = provideState({
initialState: () => ({
finalized: false
}),
effects: {
finalize: softUpdate(() => ({ finalized: true }))
}
});
const StatefulParent = wrapParentWithState(ParentWithState);

const Root = () => (
<StatefulParent>
<StatefulChild />
</StatefulParent>
);
const wrapRootWithState = provideState({
initialState: () => ({
renderChildren: true
}),
effects: {
toggelChildren: softUpdate(state => ({ renderChildren: !state.renderChildren }))
}
});
const StatefulRoot = wrapRootWithState(Root);


describe("finalize", () => {
it("parent's `finalize` is not called when a child is unmounted", async () => {
const el = mount(<StatefulRoot />);
await el.instance().effects.toggelChildren();
expect(el.find(".parent-finalized").text()).to.equal("false");
});
});
11 changes: 11 additions & 0 deletions spec/unit/provide.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,17 @@ describe("state provider", () => {
});
});

describe("upon unmounting", () => {
it("invokes its `finalize` effect", () => {
return new Promise(resolve => {
const Stateful = getStateful({
effects: { finalize: resolve }
});
mount(<Stateful />).unmount();
});
});
});

describe("context", () => {
it("preserves parent freactal context where no conflicts exist", () => {
const context = {
Expand Down
2 changes: 1 addition & 1 deletion src/effects.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const getEffects = (hocState, effectDefs, parentEffects) => {
.then(applyReducer);

return memo;
}, Object.assign({}, parentEffects, { initialize: null }));
}, Object.assign({}, parentEffects, { initialize: null, finalize: null }));

return effects;
};
1 change: 1 addition & 0 deletions src/provide.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export class BaseStatefulComponent extends Component {
}

componentWillUnmount () {
if (this.effects.finalize) { this.effects.finalize(); }
// this.unsubscribe may be undefined due to an error in child render
if (this.unsubscribe) {
this.unsubscribe();
Expand Down