Skip to content
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Enterprise Search] Move http out of React Context and to Kea Logic; prefer directly mounting #78167

Merged
merged 7 commits into from
Sep 22, 2020

Conversation

cee-chen
Copy link
Member

@cee-chen cee-chen commented Sep 22, 2020

Summary

This PR contains 2 refactors:

  1. Part 1 of our migration away from React Context to Kea Logic, starting with the http lib (since it already exists in Kea via HttpLogic). We're making this technical choice for the following reasons:

    • React Context was great during the original MVP plugin when we didn't need a lot of shared state, but I think we've outgrown it and need to move fully to Kea at this point.
    • One reason is that we have to use a lot of native Kibana APIs directly within our Kea Logic files (examples: the http lib, the navigateToUrl method), and it's much easier usage-wise to store these in Kea directly from the get-go.
    • The other reason is that moving everything to Kea also allows us to share Kea/Redux stores between separate React apps. This is now relevant because the Kibana header menu actions is technically a completely separate app, and as such it's impossible to share our current React contexts between them without passing duplicate props.

Relevant commits: [c69103c, c76735f, efe4f3d, 3653a23, & b763147]


  1. Moving away from our LogicProvider React components, and instead preferring to mount logic files directly with props from our renderApp function.
    @byronhulcher brought this up back in [Enterprise Search] Add reusable FlashMessages helper #75901 (when I added <FlashMessagesProvider />), and I think this was the implementation he was suggesting (although please let me know if I missed the mark). What this change does is:

    • Removes the need for our initializeWhatever actions
    • Ensures that certain required values/props (in this case - the http dependency) is loaded into the logic file as soon as possible. Essentially with our initializeWhatever actions, there was a small period where state was being set where values would flash between null (or whatever default) and the passed prop. By setting value defaults to props.whatever:
      • We load values in immediately to logic files - no state changes, and as such fewer rerenders
      • Removes defaults for any values/dependencies that are crucial - if the logic is mounted incorrectly or if the prop is missing or undefined, Redux will just crash :) A bit drastic admittedly, but allows us to write code knowing for sure we can depend on on certain values (a discussion we'd previously had here in a prior PR).
    • Decouples mounting our logic from React lifecycles (less uncertainty, fewer extra files/components)

Relevant commits: [16f9c73 & 2d13707]


This PR touches a lot of files and contains test refactors as well - I strongly recommend following along by commit. I hopefully left some semi-helpful commit messages explaining my process & thinking as well.

QA/Regression testing

  • HTTP calls (to our API endpoints, to telemetry actions) still 200/work as expected
  • HTTP listeners work as expected:
    • Changing to read-only mode and then navigating between AS & ES plugins
    • Stop Enterprise Search and navigate between plugins, error connecting message should pop up
  • Flash Messages still work as before (requires modifying some code directly to test)

Checklist

@cee-chen cee-chen requested review from a team September 22, 2020 15:59
@cee-chen
Copy link
Member Author

cee-chen commented Sep 22, 2020

Oops dang I hit ctrl+enter and that created the PR before I was ready to open it. Will edit the description and fix merge conflicts in a bit

@cee-chen cee-chen added Feature:Plugins release_note:skip Skip the PR/issue when compiling release notes v7.10.0 labels Sep 22, 2020
- removes need for initializeHttp call
- ensures http value is loaded into HttpLogic as soon as possible / should never load in as null, reducing # of rerenders/checks

see: https://kea.js.org/docs/guide/advanced#mounting-and-unmounting
- Kea mock import should now contain mock default values which can be overridden
send_telemetry:
- refactor to use shallow (w/ useEffect mocked) vs mount
- check mockHttpValues directly

engine_table:
- refactor to use mount w/ an I18nProvider rather than mountWithContext helper (which we'll likely need to overhaul in the future)
- assert mockHttpValues directly
EngineOverview:
- Change use of FormattedMessage to i18n.translate (simpler, no provider required)

Tests:
- Create mock values/actions for FlashMessages, since EngineOverview calls it
- Create combined mockAllValues obj for easier overriding
- Create setMockValues helper for easier test overriding (credit to @scottybollinger for the idea!)
- Update engine_overview tests to setMockValues instead of passing context to mountWithAsyncContext
- Fix mountWithAsyncContext to accept an undefined obj
- it should now only live in HttpLogic 🔥
…rops

- send history as prop
- refactor out now-unnecessary listenToHistory (we can just do it directly in afterMount without worrying about duplicate react rerenders)
- add mount helper

Tests:
- refactor history.listen mock to mockHistory (so that set_message_helpers can use it as well)
- use mountFlashMessagesLogic + create an even shorter mount() helper (credit to @JasonStoltz for the idea!)
- refactor out DEFAULT_VALUES since we're not really using it anywhere else in the file, and it's not super applicable to this store
- update history listener tests to account for logic occurring immediately on mount
@cee-chen
Copy link
Member Author

OK, I think this PR is ready for review! 🙇‍♀️ It touches a lot of files and contains test refactors, so I strongly recommend following along by commit. I left (hopefully) helpful commit messages explaining my process & thinking throughout.

@cee-chen
Copy link
Member Author

cee-chen commented Sep 22, 2020

Something I want to clarify as well - for 2., I'm not suggesting we should move to using props for any of our upcoming migrated views or components, just for shared dependencies/libs that need to be stored at plugin init (i.e., only for logic files being instantiated at renderApp in the first place).

Ideally at the end of this series of refactors, our final renderApp JSX should look like this:

<I18nProvider>
  <Provider store={store}>
    <Router history={params.history}>
      <App {...initialData} />
    </Router>
  </Provider>
</I18nProvider>

... with all our logic/context initialization being handled above ReactDOM.render() as standard JS.

Nice and clean, right??

Copy link
Contributor

@scottybollinger scottybollinger left a comment

Choose a reason for hiding this comment

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

LGTM! Thanks for the super clean commit history!

Comment on lines +45 to +49
import { useValues } from 'kea';

export const setMockValues = (values: object) => {
(useValues as jest.Mock).mockImplementation(() => ({ ...mockAllValues, ...values }));
};
Copy link
Member Author

@cee-chen cee-chen Sep 22, 2020

Choose a reason for hiding this comment

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

Oh, I totally forgot to give you credit for this @scottybollinger! I stole this from your awesome overview mocks 😁

@kibanamachine
Copy link
Contributor

💚 Build Succeeded

Build metrics

@kbn/optimizer bundle module count

id value diff baseline
enterpriseSearch 275 -2 277

async chunks size

id value diff baseline
enterpriseSearch 510.2KB -2.6KB 512.8KB

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

Copy link
Contributor

@byronhulcher byronhulcher left a comment

Choose a reason for hiding this comment

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

This looks in order, I'm glad to see this using our Kea Logic pattern vs the provider pattern in place

@scottybollinger
Copy link
Contributor

@byronhulcher can we get a ✅ if everything looks good?

@cee-chen
Copy link
Member Author

Thanks y'all!! 🍕

@cee-chen cee-chen merged commit 42026cb into elastic:master Sep 22, 2020
@cee-chen cee-chen deleted the context-to-kea branch September 22, 2020 19:33
cee-chen pushed a commit that referenced this pull request Sep 22, 2020
…prefer directly mounting (#78167) (#78204)

* Remove HttpProvider in favor of mounting HttpLogic directly w/ props

- removes need for initializeHttp call
- ensures http value is loaded into HttpLogic as soon as possible / should never load in as null, reducing # of rerenders/checks

see: https://kea.js.org/docs/guide/advanced#mounting-and-unmounting

* Update simplest components using http for sendTelemetry

* Update simplest tests for components using HttpLogic + default Kea mocks

- Kea mock import should now contain mock default values which can be overridden

* Update moderately complex tests using HttpLogic

send_telemetry:
- refactor to use shallow (w/ useEffect mocked) vs mount
- check mockHttpValues directly

engine_table:
- refactor to use mount w/ an I18nProvider rather than mountWithContext helper (which we'll likely need to overhaul in the future)
- assert mockHttpValues directly

* Update EngineOverview to HttpLogic + refactors

EngineOverview:
- Change use of FormattedMessage to i18n.translate (simpler, no provider required)

Tests:
- Create mock values/actions for FlashMessages, since EngineOverview calls it
- Create combined mockAllValues obj for easier overriding
- Create setMockValues helper for easier test overriding (credit to @scottybollinger for the idea!)
- Update engine_overview tests to setMockValues instead of passing context to mountWithAsyncContext
- Fix mountWithAsyncContext to accept an undefined obj

* Remove http from KibanaContext

- it should now only live in HttpLogic 🔥

* Remove FlashMessagesProvider in favor of mounting logic directly w/ props

- send history as prop
- refactor out now-unnecessary listenToHistory (we can just do it directly in afterMount without worrying about duplicate react rerenders)
- add mount helper

Tests:
- refactor history.listen mock to mockHistory (so that set_message_helpers can use it as well)
- use mountFlashMessagesLogic + create an even shorter mount() helper (credit to @JasonStoltz for the idea!)
- refactor out DEFAULT_VALUES since we're not really using it anywhere else in the file, and it's not super applicable to this store
- update history listener tests to account for logic occurring immediately on mount
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature:Plugins release_note:skip Skip the PR/issue when compiling release notes v7.10.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants