Ionic-React - setState doesn't cause re-render - reactjs

When I call setMyState within a useEffect hook, my understanding is react should re-run the logic to choose the component (either MyPage or IonSpinner in this case), but MyPage doesn't render unless I switch to a different tab and come back (using Ionic's IonTabs).
I confirmed setMyState is running because it updates other parts of the application (ex. triggers a different useEffect) and I know MyPage isn't being rendered because I'm doing a console.log() on the first line of rendering MyPage and this log doesn't appear.
Can someone help me with why this is happening?
<IonApp>
<IonReactRouter>
<IonTabs>
<IonRouterOutlet>
<Route exact path="/:tab(MyTab)">
{myState ? <MyPage /> : <IonSpinner />}
</Route>
... more code ...
EDIT:
Pretty sure it's a bug in Ionic? After useEffect runs setMyState, react does a render like it's supposed to but then the IonRouterOutlet has no children (none displayed in React Dev Tools).
I traced this back to the ReactRouterViewStack calling getChildrenToRender() which creates const viewItems. This viewItems object is empty {} because the viewStack is empty {}. I don't know how the viewStack is meant to be populated so I'm not sure where to go from here, but I think addViewItem is not running?
When I go to another tab and then back to this tab, everything renders correctly and the children to IonRouterOutlet are shown in React Dev Tools.
Files:
node_modules/#ionic/react/dist/index.esm.js
node_modules/#ionic/react-router/dist/index.esm.js

try adding MyState in the Dependency array of useEffect
sample code

Related

How to use context provider with react router components?

I have browsed almost every resource on the internet and have not found a solution so please don't recommend me someone else's question that is identical because I have most likely already tried it.
Here is my container that has all of the routes. These reside in the App component which is basically the uppermost component.
Here is my provider. Basically, I have a title and when you click on the different links that take you to different views (i.e., Home, Client, Company) I do something like...
const { setHeader } = useContext(HeaderContext);
useEffect(() => {
setHeader("Company Dashboard");
document.title = "Company Dashboard"
})
As you can see the "setHeader("Company Dashboard")" should set the header. The header is actually a separate component that rerenders if the HeaderContext updates.
The Provider works btw. This is because the Header component has uses this context to set the element. So if I change the empty string to "oeijsofiej" it will display "oeijsofiej" as the title BUT it does NOT change when you click on different tabs.
I have tried to surround all of the components in a but unfortunately, that does absolutely nothing. No errors thrown and the setContext doesn't do anything. I have tried other ways of formatting the element like
<Route path="/" exact render={() => <HeaderContextProvider><Home></HeaderCOntextProvider>}/>
Unfortunately, this also does nothing. I've tried other methods and get weird errors. Not sure what I'm doing wrong I've exhausted all of my resources. Please help.

React component not rendered when sub path is called directly

I am having a Server Side Rendered React app where I use HashRouter for react routing(v5). My Routes look like this
<HashRouter basename="/">
<Layout {...config} />
</HashRouter>
<Switch>
<Route exact={true} path="/" component={LPComp} />
<Route exact={true} path={this.props.siteBanner.Red} component={bannerPage} />
</Switch>
When I hit localhost:8888/parent/ and once it get loaded and if I hit localhost:8888/parent/banner in the same window the bannerPage component renders fine.
But I hit localhost:8888/parent/banner directly(consider in a new tab), then the component is not rendered properly.
Any ideas why this is happening?
Also to add on when I hit localhost:8888/parent/banner I can see LPComp(default route) also being loaded and then it disappears suddenly and the bannerPage component renders improperly.
Thanks in Advance
The behavior is making sense, since your routes is based on a prop/state this.props.siteBanner.Red. So the first thing is to put a console.log once you enter this component.
console.log(this.props.siteBanner.Red)
In your first case, you reach this component from its parent, this way the props mostly likely is resolved.
In your second case, you reach it directly, of course also from its parent, but most likely there's no time for the props to get resolved quickly. Couple of possibilities
useEffect is to update this variable
mouse click is required to get a value
setTimeout is used to defer
callback is used to get this variable
You can say there's 50ms delay in getting this prop resolved, but you need to dig out why yourself. Dynamic route is more advanced thing, it's easy to have permanent route.
Thank you all for your timely response, I was able to solve that issue by setting location=(req.url) from the server side configured static router. Going through this example I got the bigger picture I was missing.
https://medium.com/javascript-in-plain-english/server-side-rendering-in-react-redux-ab0af31c9c4b

How to use React Router to route to another url AND re-render page with NEW component?

In App.js, I have a button that if you click, should redirect users using React-Route to another URL, /landingpagehahaha, and should render a component called LandingPage. However, neither the URL is being changed in my browser nor the correct component being rendered. The behavior right now when you click the button is that the current page gets re-rendered, not the correct LandingPage component.
The React-Route logic is placed in a function called routeChange(). I put 2 alert() statements in it which get called, telling me that it is getting inside that function. However, nothing else changes.
I have tried using this.props.history.push("./LandingPage"); in routeChange() but it doesn't get past that statement. It appears like it behaves like response.json(), which returns from the function after it runs.
I have also tried using withRouter(), but I get a weird error that I can't call Route inside Router. I was unable to resolve that issue.
// Changes route
routeChange() {
alert("HELLO BEFORE");
alert("HELLo");
return (
<div>
<Route path="/landingpagehahaha" component={LandingPage} />;
</div>
);
}
// The button that is supposed to bring user to next page
<button onClick={this.routeChange}>Go To Next Page</button>
You need to return the Redirect component from your render function, or as use the history api to push the route into your navigation stack.
The first thing you should do, is move out the route declaration, and play it higher up in your component hierachy, you have to make sure the route declaration is rendered, when you're trying to go to the route.
Instead of using the history api, you could also use the Redirect component provided by react-router. I've made a small example here.
https://codesandbox.io/s/bold-paper-kxcri

In React, OK to always call ReactDOM.hydrate instead of ReactDOM.render?

I've got code like the following where I'm calling ReactDOM.hydrate. This is shared code that sometimes gets called from the node server and sometimes in the client browser. Do I need to do anything different (then calling hydrate) when calling it on the client only. Normally, I'd call render.
const render = Component => {
ReactDOM.hydrate(
<Router history={browserHistory}>
<FullPage />
</Router>,
document.getElementById('root')
)
}
render(App);
hydrate do works similar to render on client side whether the HTML has server rendered markup or not, but when there is no markup previously like not SSR then hydrate produces some warnings but, it will render your markup as expected.
A better way to solve this would be to check if its SSR (assuming root as your parent div id) :
var isMarkupPresent = document.getElementById('root').hasChildNodes();
and then you can either render or hydrate:
isMarkupPresent ? hydrate(...) ? render(...)
Strictly speaking, no it is not safe to always use ReactDOM.hydrate().
From the docs on hydrate, you should only use it on "a container whose HTML contents were rendered by ReactDOMServer". hydrate also expects that the server rendered markup is identical to what the client side render outputs, and any differences should be considered bugs.
ReactDOM.render() on the other hand is used to render your app into an empty container on the client. You may need to do this if you don't have server rendered markup on all pages.
Because render() handles a use case that hydrate() does not, it is not safe to say "you can always use ReactDOM.hydrate()".

Does React have keep-alive like Vue js?

I made a Todo list with React js. This web has List and Detail pages.
There is a list and 1 list has 10 items. When user scroll bottom, next page data will be loaded.
user click 40th item -> watch detail page (react-router) -> click back button
The main page scroll top of the page and get 1st page data again.
How to restore scroll position and datas without Ajax call?
When I used Vue js, i’ve used 'keep-alive' element.
Help me. Thank you :)
If you are working with react-router
Component can not be cached while going forward or back which lead to losing data and interaction while using Route
Component would be unmounted when Route was unmatched
After reading source code of Route we found that using children prop as a function could help to control rendering behavior.
Hiding instead of Removing would fix this issue.
I am already fixed it with my tools react-router-cache-route
Usage
Replace <Route> with <CacheRoute>
Replace <Switch> with <CacheSwitch>
If you want real <KeepAlive /> for React
I have my implementation react-activation
Online Demo
Usage
import KeepAlive, { AliveScope } from 'react-activation'
function App() {
const [show, setShow] = useState(true)
return (
<AliveScope>
<button onClick={() => setShow(show => !show)}>Toggle</button>
{show && (
<KeepAlive>
<Test />
</KeepAlive>
)}
</AliveScope>
)
}
The implementation principle is easy to say.
Because React will unload components that are in the intrinsic component hierarchy, we need to extract the components in <KeepAlive>, that is, their children props, and render them into a component that will not be unloaded.
Until now the awnser is no unfortunately. But there's a issue about it in React repository: https://github.com/facebook/react/issues/12039
keep-alive is really nice. Generally, if you want to preserve state, you look at using a Flux (Redux lib) design pattern to store your data in a global store. You can even add this to a single component use case and not use it anywhere else if you wish.
If you need to keep the component around you can look at hoisting the component up and adding a "display: none" style to the component there. This will preserve the Node and thus the component state along with it.
Worth noting also is the "key" field helps the React engine figure out what tree should be unmounted and what should be kept. If you have the same component and want to preserve its state across multiple usages, maintain the key value. Conversely, if you want to ensure an unmount, just change the key value.
While searching for the same, I found this library, which is said to be doing the same. Have not used though - https://www.npmjs.com/package/react-keep-alive

Resources