React & React-Router & ReactCSSTransitionGroup - reactjs

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.

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: Update a self-contained grandchild from a click even from grandparent

I currently have the following situation:
<GrandParent> // click happens here
<Parent>
<Child> // self-contained with its own state
Is there a way to update the react state inside of the child with a click in the GrandParent? I hate having all this Child-specific code in GrandParent.
Right now, the only way I know to make the code functional is to pull all state into GrandParent and make state changes when the click happens, then pass that state into the child (vis useContext hook).
Update:
So I researched this further and yes, I could use solutions like redux, however this feels overkill for a small app. I did find some non-library solutions that might be a good fit for anyone looking at this question in the future.
The following solution uses 'this' binding. It works even if it's a bit janky. But the downside is that you have a global name space pollution and The grandparent has to have access to the grandchild, which I don't (it's on a different 'page').
https://www.codeproject.com/Tips/1215984/Update-State-of-a-Component-from-Another-in-React
This approach uses an event emitter to signal when a state change should happen. It's pretty clean but uses a library, albeit a tiny one. I like this approach, however, it doesn't feel very 'reacty'.
https://medium.com/#krzakmarek88/eventemitter-instead-of-lifting-state-up-f5f105054a5
Finally, I ended up using this solution. The gist is that instead of using a react context for storing state, you use the context to store functions that trigger state changes. Since the callbacks don't change themselves, you avoid unnecessary redraws and the solution stays within the bounds of normal react.
https://javascript.plainenglish.io/dispatch-an-action-to-update-a-sibling-within-react-e98f5b5041f3
You could use a top-level state management tool like React Redux: https://react-redux.js.org/introduction/getting-started

How to pass data from grandchild to parent component in React?

I have a form (Parent) which contains an input field (Child), which gets its value from a reference table (Grand-grand-child) that is displayed as a modal (Grand-child) which opens up by clicking a button attached to the input field. This is a nested structure that roughly looks like this:
I need to set the value of the input field by selecting a row in the reference table and confirming my choice with a button "SET VALUE", which means I need to pass data three levels up from Grand-grand-child to Parent through Grand-child and Child.
My state is kept in the Parent component. Is there a simple way of achieving that without using external libraries? Please offer a solution using Hooks as all of my components are functional components.
Here is the code: https://codesandbox.io/s/festive-fast-jckfl
See CreateRate component where:
CreateRate.jsx is the Parent
InputField.jsx is the Child
DataFetchModal.jsx is the Grand-child
Airports.jsx is the Grand-grand-child
Pass a change handler function from parent (where state lives) down to the grand child. The grand child should call this change handler when clicking the Set Value button.
If this is too much prop drilling
look into component composition first
if that doesn’t work out, look into context api
Update:
You mentioned your problem was trying to access the state inside Grand-grand-child from your Grand-child. In this case you can lift the state up (to Grand-child). This means lifting 'airports' up to DataFetchModal. Here is more info on lifting state.
https://reactjs.org/docs/lifting-state-up.html#lifting-state-up
Also, it appears you are running into these problems because your code is very nested and not very composable. I would suggest looking into how you could better break up your components. One way to accomplish this is using compound components.
https://kentcdodds.com/blog/compound-components-with-react-hooks
Determining how to break this up better would take some time. However, just looking at this briefly. You may be able to move your DataFetchModal to your parent. Pass a callback to the InputField to fire it off and send the identifying parameters (what input called it). Then, from the parent, compose in whatever modal body using composition. It appears you have a lookup object (objType) inside DataFetchModal. Maybe this could go away by using composition (not sure, and probably separate topic).
Update 2:
I pulled down your code sandbox and made a few changes that may help access parent level state. I moved the modal into the parent. Check it out.
https://github.com/nathanielhall/Test

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.

contenteditable div caret position issue with reflux but not flux

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.

Resources