ReactiveSearch components and passing state/props - reactjs

I have a ReactiveList component that lives in my SearchContainer. All it does is display search results based on user query. The ReactiveList component works, it automatically displays the contents of my ES index (hosted at Appbaseio). It DOES NOT display results based on user submitted query.
In my global Navbar I have a DataSearch component (conditionally rendered based on route) that provides autocomplete. I also have a pretty much identical DataSearch component in my homepage at ('/').
I'm trying to figure out how to pass the query from DataSearch to ReactiveList so that the results displayed are actually based on a user submitted query?
So let me reiterate, I'm trying to figure out how to get DataSearch and ReactiveList to talk to each other.
In my DataSearch component, I have
<DataSearch
componentId="q"
URLParams={true}
onValueSelected={(value, cause, source) => {
if (cause !== 'SUGGESTION_SELECT') {
<Redirect to={{
pathname: '/search',
search: `?q=${value}`
}}/>;
} else {
this.valueSelected = true;
}
}}
...
/>
In my ReactiveList component I have:
<ReactiveList
componentId="q"
URLParams={true}
onNoResults="Sorry, no results were found, try searching again."
react={{ or: ["q"] }}
onData={(res) => { ... }}
...
/>
From what I understand, as long as I have URLParams={true} in DataSearch and componentId='q' in both DataSearch and ReactiveList - search should work. Autocomplete seems to be functional but ReactiveList is NOT displaying results based on user query.
Do I need to add another property to DataSearch or ...? Where am I going wrong.

Different components should not have the same ID. Set the componentId for your ReactiveList to something unique (and choose a different unique ID for your DataSearch).
Not sure if there is another problem in the setup but this is something you can fix right away.

Related

Changing Query paramers while staying on the same page without reload= NextJS Router

for a project I am working on I am running into a problem with the nextjs Router.I have a component that has an input field which the user should be able to input their searchterm in. There is a different component which should be able to get this searchterm and perform a search.
Because the two components aren't connected I would like to set the queryParameters in the router in the Input component, and then execute a function in the search component when the searchTerm is changed.
The problem lies in the following: The searchComponent receives the nextJS router as props and will only execute my useEffect function when those props are changed (and react knows they are changed), on top of that I need to stay on the same page when updating the query parameters, but the route of this page is dynamic. For example: the user can add this combination of components on /search but also on /lookforitem.
I have tried setting the queryParameters in the following way in the Input component:
function setQueryParams() {
router.query = {
...router.query,
searchTerm: input.current,
};
}
In combination with the following code in the Search component:
useEffect(() => {
console.log('Router has changed');
}, [router]);
The problem is that this useEffect doesnt get called untill the search component is rendered again (I have created a button that logs the router to the console, and it shows the updated router), which I assume is because React hasn't realised that the Router props have changed.
I have also tried setting the query parameters via a router.push in the following way:
function setQueryParams() {
router.push(
{
pathname: router.route,
query: {
...router.query,
searchTerm: input.current,
},
},
undefined,
{ shallow: true }
);
}
However this comes with its own set of problems. First of all it causes a refresh of the page, which I don't want. On top of that it changes the url to for example: /search?searchTerm=Hello which means that if I enter a different input and submit it will stack making the next url for example: &searchterm=hello?searchterm=goodbye.
I want a way to update the query parameters without refreshing the page, but while also notifying the other components that use the router that the query parameters have updated. All of the searching that I've done seems to be specific to either routing to a different page or routing to a predefined page.
Any help would be greatly appreciated.

passing mapped data from an api to another component

so i have data that i get from an api in componentDidMount, and then i map over it in the render. lets say the map returns 4 objects. How do i create a button click event that captures that specific objects data, and passes it along a route to another component?
code:
clickEvent(){
???
}
this.example = this.state.data.slice(1).map((data, key) =>
<div key={item.Id}>
<div>{data.dataIWantToPass}</div>
<Link to='/next_component_path' onClick={clickEvent}}>Click</Link>
</div>
So lets say the above returns 4 objects. I want the third one, when the link is clicked to pass its data.
clickEvent(dataUWantToPass){
<NewComponent dataPassed={dataUWantToPass} />
}
this.example = this.state.data.slice(1).map((data, key) =>
<div key={data.Id}>
<div>{data.dataIWantToPass}</div>
//Link is a react-router-dom component, this helps you to redirect to other component (to which you have added the route for
<Link to='/next_component_path'>
<button onClick={()=>this.clickEvent(data.dataIWantToPass)} value="Click"/>
</Link>
</div>
))
You can receive the data in the NewComponent as props.
If you want to check than you can write the componentWillReceiveProps() method in the NewComponent as:
componentWillReceiveProps(reveivedProps){
console.log(reveivedProps);
}
Ok, I have solved this by piecing together various bits from comments, so thank you for your contributions. This is how it was resolved:
On the link tag i did this:
<Link to={{ pathname: '/path', query:{passed_id: data.id} }}></Link>
Then on the component that the path routes to:
this.setState({passed_id: this.props.location.query.passed_id});
And that gave me access to the data i was passing, in this case an ID. Now I just need to figure out how to compare that ID, with the looped data I also pass (via local storage), and map over it. Another question i suppose.
You need to use redux. This allows you to store data in global application store, and get the data from any component which is subscribed to the store. Today almost every react project needs redux to manage the data across the application

How to manage state between React router and redux

I have data that I'd be fetching from an api stored as items,
{ '001': {id: '001', name:'foo',color:'blue' }}, '002': { id: '002',name:'bar',color:'orange' }, '003': { id: '003',name:'baz',color:'pink' }}
which would be added to my redux state tree and would determine what's rendered on a particular path. For example, going to /foo would directed a user to a page with a background color of its associated color, blue. I have some code I started on trying to implement this but am confused as to how I'd coordinate this. I was looking at possibly doing something like running Object.keys(props.items).find(id => props.items[id].name === match.params.name), but figure that this wouldn't be a super scalable solution and would lead to a loss of the O(1) lookup afforded by having an object. The code is here: https://codesandbox.io/s/n562wzzmkm
If you want to use the name property as the path to each item, then the method of matching (searching/finding) that you mentioned is, in my mind, probably the best option. Because name is down one level for each item, we have to search all of our items for a match to match.params.id onClick in order to render a <Detail/>s component with the correct item's data.
But, if you're open to it, I'd propose you use each item's top-level key (i.e. 001, 002, 003) as the the to prop for your mapped <Link/> components. In that way, we can still achieve O(1) by using that method in conjunction with the render prop for your <Route/> that handles rendering a <Detail/> component with the chosen item's data. We'll still do the lookup using match.params.id, but we'll only have to be looking at the top level of items to find the correct item and successfully set the correct background color.
The code in question below and a fork of your CodeSandbox: https://codesandbox.io/s/xl8von46nw?fontsize=14
/**** Setting each to prop equal to the top-level id for each item ****/
<ul>
{Object.keys(items).map(id => (
<li key={id}>
<Link to={id}>item {items[id].name}</Link>
</li>
))}
</ul>
/**** I used connect() in routes.js to access items ****/
<Route
path="/:id"
render={({ match }) => (
<Detail
item={items[match.params.id]}
/>
)}
/>
Hope that helps or is an acceptable solution!

Reactivesearch - search from any route

I'm experimenting with ReactiveSearch and using the ReactiveList component.
I'm trying to figure out how I can navigate though my React app and still be able to search.
Say I'm on the home route '/', use DataSearch component for autocomplete and that takes me to the results page '/results but I don't get any results!!
In order to get results, I have to search and stay on the '/results' page. Is there a prop that I'm missing on my DataSearch component in the Header:
<DataSearch
componentId="SearchSensor"
dataField={["original_title"]}
className="search-bar"
showIcon={true}
iconPosition="right"
innerClass={{
title: "text-title",
input: "text-input",
list: "text-item"
}}
/>
Again, in order to get results, I have to search from the DataSearch component that I have on the results page. Search doesn't work when I have search from the DataSearch component that is in the Header.
So how can I set it to be able to perform a search from any route that I might be on (searching from the DataSearch component that is in the Header for instance)?
Here you would be only using the DataSearch for autocomplete and then redirecting to the results page for the actual results. For this you could use the onValueSelected prop on DataSearch docs:
onValueSelected is called with the value selected via user interaction. It works only with autosuggest and is called whenever a suggestion is selected or a search is performed by pressing enter key. It also passes the cause of action and the source object if the cause of action was 'SUGGESTION_SELECT'.
So you can get the selected value on the homepage, and then use it for doing a search on the results page. This value can be stored in your component's state for doing search or you can add a DataSearch in your results page too and use the URLParams prop to set its value from the url. For example:
Home Page
<DataSearch
...
componentId="q"
onValueSelected={(val) => Router.push(`?q=${val}`)} // using nextjs router here for example
/>
Results Page
<DataSearch
...
componentId="q" // will set its value from the param 'q'
URLParams
className="hidden" // adding some CSS to hide it if not needed
/>
<ReactiveList
...
react={{
and: ['q']
}}
/>
Alternatively, you could use the onValueSelected on homepage to set the selected value in state (or store) and then query the ReactiveList on results page with defaultQuery prop docs.

React Router: Query Param Match?

According to the accepted answer to this question, React Router 4 doesn't match query parameters anymore. If I go from a URL matched by one of my <Route>s to the same URL with a different query string, the content doesn't seem to change. I believe this is because navigating between URLs that match the same <Route> doesn't change the content, but please correct me if I'm wrong. Given this, how do I use React Router for a set of URL's that need to differ only by query parameter?
For example, many search engines and other sites that use search bars, including the site I am working on, use a query parameter, commonly q or query. The user may search for one thing, then decide that is not what he/she wants and search for another thing. The user may type in the second URL or search with the search bar again. There isn't really a place for the search term in the URL path, so it kind of needs to go in the query string. How do we handle this situation?
Is there a way, with React Router, to link to a URL that only differs in the query string and change the content, without refreshing the entire page? Preferably, this wouldn't require any external library besides React and React Router.
Try the render function prop instead of component prop of Route. Something like this:
<Route render={props => {
// look for some param in the query string...
const useComponentA = queryStringContains('A');
if(useComponentA) {
return <ComponentA {...props}/>;
} else {
return <ComponentB {...props}/>;
}
}}/>
There are 2 ways to do that:
1) Use location.search in react component to get the query string, then pass it to child component to prevent re-rendering the whole component. React-router has the official example about this.
2) Define a regex path of router to catch the query string, then pass it to react component. Take pagination as an example:
routes.js, for router config you can refer this
const routerConfig = [
{
path: '/foo',
component: 'Foo',
},
{
path: '/student/listing:pageNumber(\\?page=.*)?',
component: 'Student'
},
Student.js
render() {
// get the page number from react router's match params
let currentPageNumber = 1;
// Defensive checking, if the query param is missing, use default number.
if (this.props.match.params.pageNumber) {
// the match param will return the whole query string,
// so we can get the number from the string before using it.
currentPageNumber = this.props.match.params.pageNumber.split('?page=').pop();
}
return <div>
student listing content ...
<Pagination pageNumber = {currentPageNumber}>
</div>
}
Pagination.js
render() {
return <div> current page number is {this.props.pageNumber} </div>
}
The 2nd solution is longer but more flexible. One of the use cases is server sider rendering:
Apart from the react components, the rest of the application (e.g. preloaded saga) need to know the url including query string to make API call.

Resources