How to simulate transient events in React / Redux?

While React with Redux works great when modeling the state of the user interface, sometimes there are situations when something happens simply, the user interface must handle this event in a discrete procedural way, and there is no point in thinking of this transient event as part of the state that will be retained for any period of time.

Two examples from the JS Bin-like code editor application:

  • The user exports his code to GitHub. When the export is complete, we want to open a new browser window that displays the essence. Thus, the React component hierarchy should know the identifier of the entity, but only at one point in time, after which it will open a window and stop worrying about the whole export.
  • The user clicks on the error message, because of which the editor displays a line in which the error occurs in focus in the editor. Again, the user interface only cares about which line should be focused for one moment in time, and at that moment the editor (not based on React) is invited to concentrate the line, and all this is forgotten.

The smallest unsatisfactory solution I came up with is:

  • If a start event occurs, send an action to update the Redux state with the necessary information (gist identifier, focus line)
  • A React component that is interested in this information will track the relevant details on the lifecycle hook ( componentWillReceiveProps , etc.). When the information appears in the details, it takes the appropriate action (loads the gist window, focuses the editor on the line).
  • The component then immediately sends another event to the Redux store, which says: "I processed this." Transient event data is removed from the Redux state.

Is there a better example for this kind of situation? I think that one of the fundamental parts of the picture is that the response of the user interface to the action always breaks out of the structure of the React components - it opens a new window by calling the method of the APIs of the editors, etc.

+5
source share
2 answers

Your solution will certainly work, but the problems that you cause are not well suited for working with reduction. Just using simple React and passing the necessary functions to your component sounds a lot more natural to me.

In the case of export, for example, instead of sending an action that updates some state, which then launches a new window to open, why not just open a new window instead of sending this action? If you have the information necessary to send an action to start opening a window, you should simply open the window in the same place.

In the example, when clicking an error message calls a non-React, imperative api call, pass the function from the nearest common parent of the error message and the editor. The parent can also maintain a wrapper link around the editor. Even if it’s several levels in depth, it’s not so bad to get a link to what you want if you miss the function to set ref . Thus, the function passed from the parent to the component of the error message can simply call the method in ref, which it supports the editor. Basically, something like this:

 class Parent extends Component { constructor(...args) { super(...args) this.onErrorMessageClick = this.onErrorMessageClick.bind(this) } onErrorMessageClick(lineNumber) { if (this.editor) { this.editor.focusOnLine(lineNumber) } } render() { return ( <div> <ErrorMessage onClick={ this.onErrorMessageClick } lineNumber={ 1 } /> <ErrorMessage onClick={ this.onErrorMessageClick } lineNumber={ 2 } /> <EditorWrapper editorRef={ (editor) => { this.editor = editor } } /> </div> ) } } const ErrorMessage = ({ onClick, lineNumber }) => ( <button onClick={ () => onClick(lineNumber) }> { `Error Message For ${lineNumber}` } </button> ) // Just adding EditorWrapper because your editor and error messages probably aren't right next to each other const EditorWrapper = ({ editorRef }) => <Editor ref={ editorRef } /> class Editor extends Component { focusOnLine(lineNumber) { // Tell editor to focus on the lineNumber } render() { ... } } 
+1
source

I often use redux-thunk for these types of events.

Essentially, this is no different from setting up a regular thunk, only I don't send an action to it.

 const openGist = (id) => () => { // code to open gist for `id` } 

Then I use this action creator, like any other component that runs it, for example. displayed in mapDispatchToProps and called in the onClick handler.

The general question that they ask me is why I just do not put this code directly in the component itself, and the answer is simple - testability . It is much easier to test a component if it has no events that cause side effects, and it is easier to test code that causes side effects in isolation from everything else.

Another advantage is that most often UX designers will come in at some point and want to get some feedback for the user for some events of this nature (for example, briefly highlight the error line), so adding an X_COMPLETED action to the event is much easier at this stage .

+1
source

Source: https://habr.com/ru/post/1267163/


All Articles