contenteditable div caret position issue with reflux but not flux - reactjs

newby alert
I am trying to use a contenteditable div in a react component hierarchy. I am hoping to use reflux instead of flux as I like the way one sets up actions and stores better in reflux (much less boilerplate, more features).
However, I am having an issue with contenteditable divs with an onInput handler attached. I'd like to keep my store up to date, so any onInput that occurs sends an action to the store, updating a field storing what is in the contenteditable div.
See the following gist, https://gist.github.com/dcnieho/0525f1becf1838616f5d, where test.js has three different ways to use an external store, changes to which lead to a setState at the top component, and this state gets passed down as props.
Using modes 1 and 3 (flux, or very simple handrolled version), things work fine: when i bash keys as fast as i can, the caret stays where it should be. When using mode 2 (reflux), the caret position will jump to the beginning of the content editable div every now and then. What i see in the log generated by my code is that the props coming in are behind the DOM content as accessed by the innerHTML through a react ref. That triggers a rerender of the contenteditable div, changing its innerHTML.
Note that things work fine with flux even when commenting out the shouldComponentUpdate function at line 92 and the componentDidUpdate at line 105, which were styled after this answer. The same problem occurs when these are commented out in mode 2, reflux.
It thus appears that rerendering per se is not an issue (else all methods would have a problem when commenting out shouldComponentUpdate), but rerendering and putting a different innerHTML that what is at that moment in the DOM into the contenteditable div messes things up.
One further insight into this is the following. I log messages to the console indicating when things of interest happen. With vanilla flux, bashing keys, I get a nice chain of onInput being called, dispatcher called, render train, next onInput called. With reflux, i may get onInputs anywhere, before render train is finished, or even before the dispatcher (well, action/store its called in reflux) is called for the previous onInput. It seems like some things happen asynchronously in reflux where they shouldn't for my use case?
Am I doing something wrong? Is the way in which event happen in reflux not suitable for what i want? Is there a better way to approach content editable divs in react+flux? Are there important concept I seem not to understand (I am a newbie)?

Ok, turns out this was a RTFM situation: reflux gives you the option of having actions triggering the store synchronously or asynchronously. The default is asynchronous. Reflux actions have a triggerAsync and a trigger method, where the former is called by default, but the latter can be called directly or set as the default when creating the action.
Synchronous handling of actions solves my problem, not allowing the DOM to get ahead of the event processing.

Related

In React, is it possible to memoize a list of components and their internal state?

[This is the Codesandbox with all of the code that exemplifies the issue I'm having. Sorry, SO doesn't allow for proper React code with TSX, etc.]
I have some tabs, naturally, whenever I click on each of them, my state changes (namely selectedTab) and the body that's being rendered. Each tab item has a body that goes with it, the first tab, in my case, has a component that, if you click, a count is increased. Well, whenever the tab changes, that count gets reset. In fact, it's a full-rerender all around.
The problem with doing a full re-render is that - not only is it inefficient, given how the user's gonna use it, but it also adds fuel to the fire: what if the user inputs something on a tab, then switches to another tab to do something else and then back to the original one? He's just lost what he typed in.
Naturally, this can be solved if we render all of the tabs' content upfront and we just play with display: none, and it'd be a good solution, but I'm also trying to learn.
I understand how React re-renders things and why this happens, but is there a way to fix it?
In my case, if you look at useTabs, the content variable simply looks at the corresponding tab's component key and takes outputs that. It's there that, I think, I need to memoize things.
Uhm, I guess you can't prevent calling your custom hook, this will lead to invariant violation error.
These hooks in the component will be executed on each render -> thus 'resets' your tabs.
You should rely on useEffect dependencies argument to run conditional code to see if values needs to be changed.
as long as your in the same app you could use contextApi,react context docs otherwise you can use localstorage, localstorage tutorial
or just create this state in the parent component and pass it down as props?

React Native how to use React.memo on every component from collection to render only changed ones. Strange behavior of React.memo

I have been trying to improve rendering performance by using React.memo on components. It works properly until I use it on every single component from the collection being inserted as children.
I prepared snack HERE
To understand what is going on you need to open the console because there are some logs presented.
You will see WrapperMemo which generally renders Child components (they are also wrapped in some auxiliary components to check something I will explain later).
When you press the first button named SLICE MYNUMBERS you will see that React.memo's isEqual function called isWrapperEqual returns true, but it still does not stop from rerendering Child components.
However neither Wrapper nor all other auxiliary wrapper components (ChildOk) are rendered!
When you click any PRESS ME:[number] button it forces you to re-render other Child components when only one is being modified.
It is worth mentioning that TestCompMemo is never re-rendered when any of the buttons are pressed - which is correct behavior.
1) Is there any explanation for that?
2) Is there a way to use React.memo on every component from collection to render only changed ones (as it is presented in Wrapper component) ?
Is there any explanation for that?
In your snack example, you have a Context.Provider wrapper at the top level of the application.
This is what causes the re-render. This is expected behavior and you can read more about it here: https://github.com/facebook/react/issues/15156#issuecomment-474590693
Is there a way to use React.memo on every component from collection to render only changed ones (as it is presented in Wrapper component) ?
Using React.memo on every component is not recommended. You can run into bugs where components do not re-render and you are adding overhead with all the comparisons.
If you want to optimize the application, you might want to run the profiler and figure out which parts of the app is slowing things down and work from there.
But if you want to prevent unnecessary re-renders, you could move context usage up a few levels and pass the onPress to the child components.

How to reset state of a child reactjs component

I have a react.js app which loads data from an API displays a bunch of questions (textboxes, radiolist, checkboxes, etc). The user fills them in and submits all answers back to the API, which then send a new set of questions.
All these questions are in a single object, so I've created parent react.js component which holds the current set of questions in state. When the state changes it re-renders each question below. This works pretty much fine.
The problem is that sometimes the API displays the exact same question for twice in a row, but as this is held in state and react.js is clever enough to know it doesn't need to render a completely new component, because the old one will do (with a few small updates).
The problem is that if I select a radio button on the first one, based on the initial data stored in state of the child component, which was initially set within componentDidMount). But when the second question comes along, because its essentially the same component, the state remains. The constructor is not called again.
I think I should be using one of the other events, perhaps:
componentWillReceiveProps
componentWillMount
componentWillUpdate
but I can't figure out which one is the most consistent one.
I basically want to reset the selectedAnswer everytime the parent has received new data from the API and essentially re-render all child components (but react won't do that).
Edit
I wonder instead of trying to reset this via the internal lifecycle events, whether I can pass in a different set of props into the component, so it can decide whether to re-create or re-render in the usual way.
okay so to optimally do this lets suppose you api which returns the set of questions, it might contain some id associated with it. Using that id create a uniq key for every child component while rendering something like below
<Child key={`${data_id}_${index}`} />
This will ensure that for the same set they do not keep mounting again and again and will only mount if a new data set is fetched in which case data_id will change which would cause remounting of each and every child component
I'd encourage you to check out Redux. It makes managing state much easier. I'd contribute some code on here but I am not sure I actually understand the question. If you linked us to your Github, then I could probably answer this specific question.
Also, it seems like you don't really need to touch state. It sounds more life attaching an event and controlling state that way. For example, using onSubmit, you can make an API call (and whatever else) and then have another function to reset the form state afterwards. It would be pretty straight forward, especially if you are using then/catch Promises.

Editing a form is not working on react redux

I am new on react. I am working on react application with redux. I have a form (I am using redux-form) by which I can save data or edit data.
my problem is , In edit mode I populate data using componentWillReceiveProps. and populated perfectly, now when I try to clear any field on form its again fill.
componentWillReceiveProps(nextProps){
this.props.dispatch(initialize('NewProject', nextProps.project.project[0]));
}
I would be grateful for any help.
Is there a reason you're not dispatching this action somewhere else, like in componentDidMount? I can't say without seeing more code, but it's possible that whenever you edit your form, React again calls componentWillReceiveProps and overwrites whatever you did with the behavior you've given your component.
Per the React documentation:
Note that React may call this method even if the props have not changed, so make sure to compare the current and next values if you only want to handle changes. This may occur when the parent component causes your component to re-render.
It may be a good idea for you to move your dispatch to a more predictable event if possible, like componentDidMount. Typically, your render method should be capable of handling different cases if a component has multiple possible states. You could also create an edit and save version of your component, and render one or the other based on the props you receive. The best way to "populate" data, as you put it, is to define propTypes for your component, and then use your props to insert that data into elements in the render method.

React & React-Router & ReactCSSTransitionGroup

I'm creating a simple app with three states and using React's router with ReactCSSTransitiouGroup. Default behavior of ReactCSSTransitionGroup is simple:
After the click to Link append/prepend new child
New child gets "app-enter" and "app-enter-active" classes
Old child gets "app-leave" and "app-leave-active" classes
After timeout remove the old child
What I'm trying to do is to change this behavior so it can behave like that:
After the click to Link stay in the same state. Just add to the old child classes "app-leave" and "app-leave-active" classes.
After the leave timeout remove the old child and append new child with "app-enter" and "app-enter-active" classes.
After the enter timeout remove it's classes
Is there some easy way how to get this? Thanks!
One simple solution would be to not use a traditional link. If you add a custom event listener you can just start a timeout with startTimeout(namedFunctionToSetLocation, 200ms). I strongly recommend being frugal and using flags to control the state complexity, which will otherwise try and eat you alive.
In more detail:
When someone triggers your event listener, firstly set some state which will add your classes "app-leave" and "app-leave-active". You can do this in a separate function but make sure to bind the function to your component's this so it can access this.state, or pass the state in when rendering.
In that function, you also want to start a timeout, so you can do the next you do of changes after a delay. It's worth generating an ID at this point, or setting a flag, so you can check after the delay that nothing else has begun in that time (if you use one ID variable for all transitions, and just update every time a new one begins, you should have a rock solid check whether, after an async delay, you still care about continuing).
When the timeout is triggered, and you've checked your flag, you can manually trigger your router, or set a new location, however you like.
I've found it useful to use a simple bespoke approach when ReactTransitionGroup isn't a perfect fit. The transition group has nuanced behaviours with ReactRouter v4, and it's completely unnecessary if all you need is a delay or simple class change on click.
Finally, you can also use the normal ReactRouter and ReactTransitionGroup components inside of conditional rendering blocks (like those I just described)- don't pass off that technique, it's as React-ee as it gets.

Resources