Reactivesearch & Reactivemaps search routing - reactjs

Recently I started using Reactivesearch and Reactivemaps by Appbase.io on my website to search my database of items and render them and so on. Most of the components work as they should and some have minor bugs. So recently I moved my component into its own component and then placed that on my nav bar for global access. The issue I then encountered was "how can I route back to my /explore page with my search value?". Evidently its not as simple as I thought.
I setup my search page with a function toPath that takes the path and the value that comes from onValueSelected
toPath(path, obj) {
this.props.history.push(`${path}?q="${obj.term}"`);
console.log('SearchVal: ',obj)
}
Note that term is added because its the object.term and term is the string from your search.
So I try this and I have URLParams={true} on my <ReactiveGoogleMap/> and <CategorySearch/> set and I select my value, and it takes me to the /explore page but my map is centered on item 0 of the elastic database.
I tried a few other methods, passing just the object, removing URLParams and so on. Nothing seems to work.
If anyone has any idea how this could be achieved I would greatly appreciate it, its just this minor issue but still a problem down the road and the documentation lacks how to do this / any examples for the public.
Here is the current config:
toPath(path, obj) {
this.props.history.push(`${path}?q="${obj.term}"`);
console.log('SearchVal: ',obj)
}
<CategorySearch
className="searchbar"
dataField={["restaurantName"]}
categoryField="city.keyword"
placeholder='Let's Search"'
componentId="q"
URLParams={true}
...
<ReactiveGoogleMap
componentId="map"
dataField="location"
stream={true}
defaultZoom={13}
pagination
autoCenter={false}
onPageChange={() => {
window.scrollTo(0, 0);
}}
style={{
}}
onPopoverClick={this.onPopoverClick}
className="rightCol"
showMarkerClusters={false}
showSearchAsMove={false}
searchAsMove={true}
//this.props.location.state.reactive_s
//The old way I passed the object I used this ^
react={{
and: [
"q"
],
}}
Also as a side note, i'm not sure if anyone else is getting this but I get these constant issues from #emotion and its a problem with Reactivesearch
serialize.browser.cjs.js:149 Functions that are interpolated in css calls will be stringified.
If you want to have a css call based on props, create a function that returns a css call like this
let dynamicStyle = (props) => css`color: ${props.color}`
It can be called directly with props or interpolated in a styled call like this
let SomeComponent = styled('div')`${dynamicStyle}`
This is also an issue with reactivesearch and makes it very difficult to work because you can't exactly hide it without hiding everything else. It happens with every click and scroll of the page that has a reactive component. Only on dev branches, never on compiled builds.

Related

Multiple MUIDialogs won't always stack in the right order when using Context in React?

I'm currently rendering multiple dialogs on the screen after the user successfully logs in. They're working great...except they won't always stack in the order that I want them too. We have it setup so the dialogs will stack on top of each other. However, it's finicky, and sometimes after a user logs in, they won't be stacked in the desired order.
I'm using React Context for displaying the dialogs globally, and react-router for global state management. I'm currently using Dialog from #material-ui/core, version 4.12.3.
I can't show too much of the code, but here's essentially what's happening in the render method:
//...there is code above
<ThemeProvider theme={theme}>
{dialogs.map(dialog => {
// dialogs defined as a const
const DialogComponent = dialogs[dialog.key].Component
if (!DialogComponent) {
return null
}
return (
<DialogsCurrentContext.Provider key={dialog.id} value={dialog}>
<DialogComponent
params={dialog.props}
data={dialog.data}
DialogProps={{
open: dialog.open,
onClose: onDialogClose,
TransitionProps: {
onExited: () => onDialogExit(dialog.id),
},
}}
/>
</DialogsCurrentContext.Provider>
)
})}
</ThemeProvider>
//...there is code below
Note: In the code, they always appear to render in the right order. We even used sorting, but still will get undesired stacking order. You'll see in the image below.
The Components tab in the React Developer Tools shows the Contexts Providers rendering order. Each one of these Context.Providers container a MUIDialog component:
Components Tab
Here is what actually rendered to the DOM : HTML from Developer Tools
Note: I can't show the dialogs themselves, but I listed which MUIDialog went where with the keys.
I clearly wanted the MUIDialog with key=4 to be on top of key=3, but from what is actually rendered to the DOM, I get key=3 over key=4.
How can this difference happen in the first place? Too me, this looks like a rendering issue, but unsure what.

Rendering issue with custom map component inside tabbed form of react-admin

I am using React-admin for a project where for some resources, I use the tabbed form to better organize the fields and inputs. I created a fairly simple custom map component based on react-leaflet, which I am using in these tabbed forms.
I am facing a weird issue where when the map is on other than the first tab, its contents do not display correctly. It appears as though the map "thinks" the viewport is much smaller than it actually is. Reloading the page (or even just opening developer tools in Chrome) forces re-render of the page and causes the map to start behaving correctly.
To better demonstrate, I created this simple Codesandbox project. It has a user resource from the RA tutorial with two tabs. Both contain an instance of the map component, but while the map on the first tab works correctly right away, the one on the second tab renders incorrectly.
I confess I am still kind of a noob at these things so I may well be doing something wrong, but I've been scratching my head for quite a few hours over this and I'd like to start eliminating possible culprits.
Any insight would be most welcome.
Thanks very much in advance for your time.
This issue has been discussed a lot in SO. If you search a bit you can find the reason. What you actually need to do is two things:
use setInterval in combination with map's invalidateSize method when switching a tab and then clean it on component unmount
use useEffect to change mapZoom and view since they are immutable.
SO since you use react-leaflet version 2.7.x you need to take the map instance using a ref:
const mapRef = useRef();
useEffect(() => {
if (!mapRef.current) return;
const map = mapRef.current.leafletElement;
const mapZoom = zoom || defaultZoom;
let intervalInstance;
if (!center && marker) {
map.setView(marker, mapZoom);
intervalInstance = setInterval(() => map.invalidateSize(), 100);
} else if (!center) {
map.setView([0.0, 0.0], mapZoom);
}
map.setZoom(mapZoom);
return () => clearInterval(intervalInstance);
}, []);
<LeafletMap
ref={mapRef}
center={marker}
zoom={defaultZoom}
className={classes.leafletContainer}
>
Demo

How to target specific components through Links in react-router?

I want to insert links throughout the content of a React app, where links trigger 'navigation' only within specific Components in the tree. Ideally I would use a react library to do this, rather than invent my own framework from scratch.
Existing approaches that I can find, such as react-router, seem to assume that every routed component should only be visible when a route path matches it, rather than routes being able to selectively send 'control' signals to matching components, while unmatching components should not be affected at all.
My intended application needs independent navigation within different panes, similar to the behaviour of a HTML Frameset ( see e.g. this JavaDoc single-page navigation - https://docs.oracle.com/javase/7/docs/api/ ) where specific links have a href (a route in React) but also a 'target' which indicates which pane needs to be affected by a given navigation.
I am aware I could write a bespoke React eventing pattern. For example I could pass hooks to make changes through tree state, with my own bespoke hash or history eventing in place to monitor clicks. Before I consider writing my own framework for this, I want to understand what other approaches there are and I think I must be overlooking something obvious.
I have put together a repository which simplifies the problem from a react-router point of view.
https://github.com/cefn/graphql-gist/tree/fde58e9cf5d321d1edf3b916da4bdd95b79751a1/react-router-frames
This app has 'Frames' with embedded links. However, every Frame's component in the React tree has to be switched out for another matching component (or none) when a Link is clicked. Ideally I should be able to add a 'target' attribute or otherwise specialise a Link or target so that only a targeted part of the tree is affected by a matching link.
It should be possible for example, to cause the color of the name='left' or name='right' frames to change independently in https://github.com/cefn/graphql-gist/blob/fde58e9cf5d321d1edf3b916da4bdd95b79751a1/react-router-frames/src/FrameSet.js
I am hoping for something from the mainstream react ecosystem which supports routing (e.g. well-documented components with hash listening, history support) but not where every Link affects every Route in the page.
Here is a solution which exploits react-router and isn't totally horrible.
function FrameSet(props) {
return <Router>
<FilterPath pathPrefix="/left/">
<View />
</FilterPath>
<FilterPath pathPrefix="/right/">
<View />
</FilterPath>
</Router>
}
A FilterPath always passes on its pathPrefix value to its childrens' props, and optionally, (when the react-router location matches the pathPrefix) passes on a pathSuffix too.
In this way, each View above only receives a pathSuffix update when a route path begins with their pathPrefix and hence 'targets' them.
A draft working implementation of FilterPath is...
function FilterPath(props) {
return <Route render={({ location: { pathname } }) => {
const { pathPrefix } = props
let pathSuffix
if (pathname.startsWith(pathPrefix)) {
pathSuffix = pathname.slice(pathPrefix.length)
}
return React.Children.map(props.children, child => React.cloneElement(child, { pathPrefix, pathSuffix }))
}} />
}
A working example using FilterPath can be seen at https://github.com/cefn/graphql-gist/blob/2de588be6c2d30b92d452f71749377c9dc6c19c7/react-router-frames/src/FrameSet.js#L22

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

Wait for element to be defined in Angular2

I'm trying to embed a Google Chart in a PrimeNG tab. I'm using a fork of the Angular 2 Google Chart package. The first time I route to the page with the chart, everything works as expected. However, when I navigate away, and return I get the following error:
Error in ./MyChartComponent class MyChartComponent - inline
template:1:5 caused by: The container #my_chart is not defined.
This is the same error one would get if they were trying to draw a chart into a div that didn't exist. As far as I can tell,
1) it works the first time because there is a delay while the charts library is retrieved, allowing for the <div id="my_chart"...> element to be rendered.
2) when the route is reopened, a sequence of events fires that results in the charting library to try and set the content of of the div before the div has been included in the DOM.
The charting components work fine when they are not embeded in the PrimeNG tabs. I suspect the order in which events occurs has been broken by this. I found a reference that said PrimeNG tabs put content into <ng-content>. If this breaks some sort of Parent/Child relationship in the components lifecycle, then I further suppose that I need some way to wait on setting the #Input to the chart component until after the "my_chart" div exists.
I've dug through the documentation for the various Angular life cycle hooks, but haven't been able to solve this issue. Any adivice would be appreciated.
Try using the resolve attribute in setting up your route:
{
path: '',
resolve: { chartData: ChartResolver },
component: MyChartComponent
}
Then in your component pull that data in:
ngOnInit() {
//The 'data' gets loaded into the snapshot by the route resolver
this.chartData = this.route.snapshot.data['chartData'];
}
Then you have to create the Resolver class:
#Injectable()
export class ChartResolver implements Resolve<ChartData> {
constructor(private http:Http) {}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<ChartData> {
//Doesn't have to be a server call, as long as it returns the data
return this.http.get("/url-to-chart-info)
.map((res:Response) => res.json());
}
}
You'll have to import everything correctly and I don't know if you have a Typescript class for the data, but something in this direction should work. This should make the data or whatever else you need to have ready available before the template is rendered.
Hopefully that helps, it doesn't fully address your question, more part 2, but the resolver is the best way to get everything ready before you need to render.

Resources