Skip to content

Commit e71f22a

Browse files
authored
Merge pull request #482 from Expensify/marcaaron-propsOnFirstRender
Render components only after we have collected the Ion props
2 parents 314bce2 + 42f3747 commit e71f22a

1 file changed

Lines changed: 40 additions & 1 deletion

File tree

src/components/withIon.js

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,18 @@ export default function (mapIonToState) {
3131
// this.props. These are stored differently because anytime the props change, the component has to be
3232
// reconnected to Ion with the new props.
3333
this.activeConnectionIDsWithPropsData = {};
34+
35+
this.state = {
36+
loading: true,
37+
};
3438
}
3539

3640
componentDidMount() {
3741
// Subscribe each of the state properties to the proper Ion key
3842
_.each(mapIonToState, (mapping, propertyName) => {
3943
this.connectMappingToIon(mapping, propertyName, this.wrappedComponent);
4044
});
45+
this.checkAndUpdateLoading();
4146
}
4247

4348
componentDidUpdate(prevProps) {
@@ -53,6 +58,7 @@ export default function (mapIonToState) {
5358
}
5459
}
5560
});
61+
this.checkAndUpdateLoading();
5662
}
5763

5864
componentWillUnmount() {
@@ -61,6 +67,31 @@ export default function (mapIonToState) {
6167
_.each(this.activeConnectionIDsWithPropsData, Ion.disconnect);
6268
}
6369

70+
/**
71+
* Makes sure each Ion key we requested has been set to state with a value of some kind.
72+
* We are doing this so that the wrapped component will only render when all the data
73+
* it needs is available to it.
74+
*/
75+
checkAndUpdateLoading() {
76+
if (!this.state.loading) {
77+
return;
78+
}
79+
80+
// Filter all keys by those which we do want to init with stored values
81+
// since keys that are configured to not init with stored values will
82+
// never appear on state when the component mounts - only after they update
83+
// organically.
84+
const requiredKeysForInit = _.chain(mapIonToState)
85+
.omit(config => config.initWithStoredValues === false)
86+
.keys()
87+
.value();
88+
89+
// All state keys should exist and at least have a value of null
90+
if (_.every(requiredKeysForInit, key => !_.isUndefined(this.state[key]))) {
91+
this.setState({loading: false});
92+
}
93+
}
94+
6495
/**
6596
* Takes a single mapping and binds the state of the component to the store
6697
*
@@ -97,13 +128,21 @@ export default function (mapIonToState) {
97128
}
98129

99130
render() {
131+
if (this.state.loading) {
132+
return null;
133+
}
134+
135+
// Remove any internal state properties used by withIon
136+
// that should not be passed to a wrapped component
137+
const stateToPass = _.omit(this.state, 'loading');
138+
100139
// Spreading props and state is necessary in an HOC where the data cannot be predicted
101140
return (
102141
<WrappedComponent
103142
// eslint-disable-next-line react/jsx-props-no-spreading
104143
{...this.props}
105144
// eslint-disable-next-line react/jsx-props-no-spreading
106-
{...this.state}
145+
{...stateToPass}
107146
/>
108147
);
109148
}

0 commit comments

Comments
 (0)