Skip to content
This repository has been archived by the owner on Mar 7, 2024. It is now read-only.

Commit

Permalink
feat: 小程序端支持 suspense (#995)
Browse files Browse the repository at this point in the history
* feat(remax): 小程序端支持suspense data fetching

* fix(remax): 修复可能带来隐患的“hideInstance中直接修改VNode#props的操作”

* test(remax): 减少suspense相关测试等待的时间

* test(remax): 更新构建fixtures
  • Loading branch information
IronLu233 committed May 29, 2020
1 parent c14e111 commit aa23f14
Show file tree
Hide file tree
Showing 4 changed files with 191 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,31 @@ var childHostContext = {};
removeChildFromContainer: function removeChildFromContainer(container, child) {
container.removeChild(child);
},
hideInstance: function hideInstance(instance) {
var _a;

var originStyle = (_a = instance.props) === null || _a === void 0 ? void 0 : _a.style;
var newStyle = Object.assign({}, originStyle || {}, {
display: 'none'
}); // 微信和阿里的小程序都不支持在内联样式中加`important!`

instance.props = Object.assign({}, instance.props || {}, {
style: newStyle
});
instance.update();
},
hideTextInstance: function hideTextInstance(instance) {
instance.text = '';
instance.update();
},
unhideInstance: function unhideInstance(instance, props) {
instance.props = props;
instance.update();
},
unhideTextInstance: function unhideTextInstance(instance, text) {
instance.text = text;
instance.update();
},
schedulePassiveEffects: scheduleDeferredCallback,
cancelPassiveEffects: cancelDeferredCallback,
shouldYield: shouldYield,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,31 @@ var childHostContext = {};
removeChildFromContainer: function removeChildFromContainer(container, child) {
container.removeChild(child);
},
hideInstance: function hideInstance(instance) {
var _a;

var originStyle = (_a = instance.props) === null || _a === void 0 ? void 0 : _a.style;
var newStyle = Object.assign({}, originStyle || {}, {
display: 'none'
}); // 微信和阿里的小程序都不支持在内联样式中加`important!`

instance.props = Object.assign({}, instance.props || {}, {
style: newStyle
});
instance.update();
},
hideTextInstance: function hideTextInstance(instance) {
instance.text = '';
instance.update();
},
unhideInstance: function unhideInstance(instance, props) {
instance.props = props;
instance.update();
},
unhideTextInstance: function unhideTextInstance(instance, text) {
instance.text = text;
instance.update();
},
schedulePassiveEffects: scheduleDeferredCallback,
cancelPassiveEffects: cancelDeferredCallback,
shouldYield: shouldYield,
Expand Down
119 changes: 119 additions & 0 deletions packages/remax-runtime/src/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -451,3 +451,122 @@ it('usePageInstance works', done => {
const container = new Container(p);
render(<Page page={{ data: {} }} />, container);
});

describe('Remax Suspense placeholder', () => {
function delay(ms: number) {
if (typeof ms !== 'number') {
throw new Error('Must specify ms');
}
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, ms);
});
}

function createTextResource(ms: number, text: string) {
let status = 'pending';
let result: any;

const suspender = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(text);
}, ms);
}).then(
r => {
status = 'success';
result = r;
},
e => {
status = 'error';
result = e;
}
);

return {
read() {
if (status === 'pending') {
throw suspender;
}

if (status === 'error') {
throw result;
}
return result as string;
},
};
}

const Text: React.FC<{ text: string }> = props => {
return (props.text as unknown) as React.ReactElement<any, any>;
};

type Resource = ReturnType<typeof createTextResource>;

const AsyncText: React.FC<{ text: string; resource: Resource }> = props => {
props.resource.read();
return (props.text as unknown) as React.ReactElement<any, any>;
};

const Suspense = React.Suspense;

it('hides and unhides timed out DOM elements', async () => {
beforeAll(() => {
jest.useFakeTimers();
});

afterAll(() => {
jest.useRealTimers();
});
const refs = [React.createRef<any>(), React.createRef<any>(), React.createRef<any>()];

function App() {
return (
<View>
<Suspense fallback={<Text text="loading" />}>
<View ref={refs[0]}>
<Text text="A" />
</View>
<View ref={refs[1]}>
<AsyncText resource={createTextResource(1, 'B')} text="B" />
</View>
<View ref={refs[2]} style={{ display: 'inline' }}>
<Text text="C" />
</View>
</Suspense>
</View>
);
}

const container = new Container(p);
render(<App />, container);
expect(refs[0].current.props.style.display).toEqual('none');
expect(refs[1].current.props.style.display).toEqual('none');
expect(refs[2].current.props.style.display).toEqual('none');

await delay(10);
expect(refs[0].current.props.style).toEqual(undefined);
expect(refs[1].current.props.style).toEqual(undefined);
expect(refs[2].current.props.style.display).toEqual('inline');
});

it('hides and unhides timed out text nodes', async () => {
function App() {
return (
<View>
<Suspense fallback={<Text text="Loading..." />}>
<Text text="A" />
<AsyncText resource={createTextResource(1, 'B')} text="B" />
<Text text="C" />
</Suspense>
</View>
);
}

const container = new Container(p);
render(<App />, container);
expect(container.root.children[0].children.map(node => node.text).join('')).toBe('Loading...');
await delay(10);
expect(container.root.children[0].children.map(node => node.text).join('')).toBe('ABC');
});
});
22 changes: 22 additions & 0 deletions packages/remax-runtime/src/hostConfig/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,28 @@ export default {
container.removeChild(child);
},

hideInstance(instance: VNode) {
const originStyle = instance.props?.style;
const newStyle = Object.assign({}, originStyle || {}, { display: 'none' }); // 微信和阿里的小程序都不支持在内联样式中加`important!`
instance.props = Object.assign({}, instance.props || {}, { style: newStyle });
instance.update();
},

hideTextInstance(instance: VNode) {
instance.text = '';
instance.update();
},

unhideInstance(instance: VNode, props: any) {
instance.props = props;
instance.update();
},

unhideTextInstance(instance: VNode, text: string) {
instance.text = text;
instance.update();
},

schedulePassiveEffects: scheduleDeferredCallback,
cancelPassiveEffects: cancelDeferredCallback,
shouldYield,
Expand Down

1 comment on commit aa23f14

@vercel
Copy link

@vercel vercel bot commented on aa23f14 May 29, 2020

Choose a reason for hiding this comment

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

Please sign in to comment.