React router -- How to send props without query params or Redux? - reactjs

I want to send data to another route, but don't want to send it in query params.
I don't want a new store for every route, nor do I want a store that simply holds all routes / params separately from where they are sent / consumed.
Is there a standard way to specify props for an upcoming route?

I found the solution on the react-router location api docs.
this.props.router.push({
pathname: '/view-user',
state: { userId }
});
This seems great for interstitial, standalone modal pages.
May need to specify a fallback if the state is missing, but haven't quite gotten that far.
if (!this.props.location.state) this.props.router.goBack();
or
const locations = this.props.location.pathname.split('/');
// do something
this.props.route.push(locations.join('/'));

If you are not sending the information in the query param, then you can put it in some other kind of store that can also be associated with the route.
You can wrap the router.push() call with your own function that takes an extra object you want to pass along. Something like...
function navigateTo(url, extraData) {
if (extraData !== undefined) {
saveExtraDataForRoute(url, extraData);
}
router.push(url);
}
In react-router, there is an onEnter prop associated with the route that specifies a function to call. The code in this function can retrieve the extra data and do whatever you want to do with it.
function onMyScreenEnter() {
const extraData = getExtraDataForRoute(url);
goCrazyNutsWithYourExtraData(extraData);
}
You'd supply the two functions saveExtraDataForRoute() and getExtraDataForRoute(). They could use your store (e.g. Redux), set values of a singleton object, or use LocalStorage. But essentially, to save the data so it's retrievable by URL later, you'd be saying something like:
extraDataStore[url] = extraData;
The other thing you may wish to look into is using a POST method with react-router. I haven't done this, and am not sure how well it works. Here is a link: How to Handle Post Request in Isomorphic React + React Router Application

Related

React query-string

I understand that the query-string library is recommended for processing the URL query strings in a React project. My question will be trivial, it's simply that as a newby I cannot work it out... How is this library receiving to be used within a functional component? Below is simply code, all I want to do is process the URL on launch and do something in response.
In all examples, this component receives a 'props', which is not a value sent into my component (I have a Props, but it is an interface which is sent in by a parent component only).
function Finder(props: Props) {
React.useEffect(() => {
const parsed = queryString.parse();
console.log(parsed);
/* Do something here */
}, []);
}
According to the documentation, query-string can use location.search as input to the parse method. In the browser, location is a global object that represents the URL of the page, and location.search returns the query string itself.

Intercepting and manipulating browser history with react router history

I'm trying to intercept history call in react app and manipulate it to change search parameter of url when it tries to route to certain path. I'm successfully on listening but changing location parameters or replacing history (I want to add ?default_param=value) is not working. It still change location using old parameters. My example code:
import createHistory from 'history/createBrowserHistory';
const history = createHistory();
history.listen(() => {
if (
history.location.pathname === '/path-to-manipulate' &&
!history.location.search
) {
const unblock = history.block();
// this one doesn't change current redirection
history.push({
pathname: '/path-to-manipulate',
search: '?default_param=value',
});
/* this one is also not working
history.replace({
pathname: '/path-to-manipulate',
search: '?default_param=value',
});
*/
unblock();
}
});
Long time ago there was history.listenBefore() https://github.com/ReactTraining/history/issues/379, but it was removed from api and now we have history.block(). I tried with and without block, with different combination of replace/push/forward and event changing history.location explicite. Nothing of that would allow me to properly intercept location before change, alter it and execute with new parameter ?default_param=value.
How to properly intercept location change and change it (location params) before it executes and redirect?
Two more things:
I don't want to mess with <Link to=...>
components and implement logic there. I wanted something global that could manage routing on higher level in react app.
listen() won't work when entering url in browser, it only works when using routing in app. Is there another way to catch first reference on certain location in react app?
What I usually do is try to read the default_param value, and if there is nothing I return with <Redirect to... />
"I wanted something global that could manage routing on higher level in react app."
Why is it? You want a global solution for a problem which is very specific?
If this problem happens a lot (you need a default param), you can write a HOC which takes a parameter and a default value and either return with a <Redirect /> or the wrapped component depends on you have the parameter in the url

How do you pass data from one view to the other via react-router-dom without using url parameters?

I use react-router-dom v 4.3.1 for client-side routing. I'm pretty new to React and can't figure out how to pass data from one view to the other without using url parameters. In Angular, the Angular router has a data property where you can pass data associated with a route. An example would be:
const appRoutes: Routes = [
{
path: 'hero/:id',
component: HeroDetailComponent,
data: { title: 'Hero Detail' }
},
];
Can you do the same in react-router-dom? If not, how would you recommend I pass data in React?
Thanks in advance for the help!
<Route path="hero/:id" render={() => <HeroDetailComponent title= "Hero Detail" />} />
Read this: Pass props to a component rendered by React Router
Or if you are using <Link> you can use pass through location object
<Link to={{ pathname: 'hero/:id', state: { title: 'Hero Detail'} }}>My route</Link>
Well you Could use the context API to create a sort of global AppState that you could update in your first component and use in your second component.
You could also abuse the localStorage API by setting a key with the data in the first component and getting it in the other.
However both of these are workarounds that Shouldn't have to be used. Why do you want to redirect to a page but not pass data to it using URL parameters.
There'a several solutions. React being a library, not a framework, doesn’t force you into a single one.
One way is to use the context api. It’s like a link to an object shared between different components.
Another one is redux, which uses context underneath, and gives you a single store for the whole app. You changes values dispatching actions to the store, so it’s a bit tricky to learn the first time.
Using a stream library would open up a lot of different options, but it’s harder to get into. Check refract if you want to go this way.
A poor man’s stream approach that may serve you is using document as a bus to pass data arround, using addEventListeners to receive data and dispatch new customEvent to send it.
Next is the simplest one of all, share a simple object. Using imports form your components, you can import the same object on both and that will be a single instance where data can be shared. Simple JavaScript. It’s not the react way though, because changes won’t trigger a repaint on the component.

When in component lifecycle should I get query params from URL?

I'm using React 16.4.1, React Router 4.3.1, and React Redux 5.0.7. I have a search route that can receive a query param like this:
https://example.com/search?q=foo
To be clear, React Router 4 discontinued support for location.query, so we're left having to manually parse query params from the location.search prop that React Router provides. We can use something like Javascript's URLSearchParams interface for this.
So I'd like a user to be able to visit the URL above and immediately begin a search for "foo". Therefore, I need to gather the q param at some point during page load. But when?
My first instinct was to have my Search component parse the query params during its componentDidMount lifecycle hook. That also happens to be the recommended hook for retrieving data from the server, something I'll do if the q param has a value.
But I've also considered moving that logic outside the component entirely to some JS file that generally runs on page load, like my app's index.js file. I have access to my Redux store there and could update the application state with the "searchText", and my Search component could then simply check for that prop (wired via Redux) during its mounting.
Gathering query params from the URL on page load - then taking action on them - is a relatively new problem for React developers, given that React Router handled it for us prior to version 4. But surely I'm not the first person to have to do this since version 4 was released. Is there an established pattern or best practice for this?
Thanks.
My approach would be to create an initialize folder along actions, reducers etc.. and create there functions like
export default (dispatch, getState) => {
dispatch(urlQueryParams());
// Some other initializers
};
const urlQueryParams = () => {
// return json to reducer with the params
}
Then on your main index file you can trigger it
import addQueryParamsInitialzer from 'redux/initialize/queryParam';
const store = configureStore(INITIAL_STATE);
addQueryParamsInitialzer(store.dispatch, store.getState);
That way you'll have it on your store no matter what component you're on

Accessing react-router from flummox action/store

I want to be able to make an API call in a Flummox action and transition differently depending on the response. I can pass the router into the action call but am looking for advice on a potentially better way.
UPDATE:
The correct answer is below but I wanted to add some detail to this.
I'm doing an isomorphic app that 1. needs to get data from an api and 2. may need to redirect based on the api response. Whatever I do needs to work through an express.js app and through react.
I made a small lib that does the api call and return some results. I pass it an object (query params object from express for the server-side or a similar object I create for the react-side). This lib makes the request, determines if a redirect is needed and passes back errors, path (string), redirect (boolean), and data (json).
In express, if the redirect boolean is true, I just redirect to it with the current query params. If it's false, I pass the data to flux through an action which updates a store. I then renderToString with react, serialize stores so the clint-side can bootstrap, and send a rendered page to the client.
In react, the redirect boolean isn't important, I get the response back from my lib, pass the data to my flux action, and just transition to whatever the path is. There's really no notion of redirection. Just go to the path no matter what.
Hopefully this is helpful to someone.
In my setup I have my own router module which just wraps the instance of react-router that I create at startup. That makes it easy for any part of the application to just require that module and do what it needs to.
But I would advise you not to have side effects like a call to the router inside your actions. Actions should concern themselves on mutating your application state, and nothing more. It should be possible to call the same action from anywhere in your application which needs to perform the mutation that the action encapsulates.
So if you're switching routes during an action, you're basically tying that action to that particular use case. Let's take an example. You have a todo list, with an input box at the bottom to add a new todo. For that use case, it might be useful to switch route after you saved the todo. Perhaps you switch to Recent Todos or something. But then a new use case comes along where you want to be able to add new todos during another workflow, perhaps the user is planning her week and should be able to add todos on different days. You want the same action that adds a todo, but you certainly don't want to switch routes because the user is still planning the week.
I haven't used Flummox a lot, but from my understanding your Flux object returns whatever the action returns when you trigger an action. So instead of putting the route change inside your action, make sure to return the response from the action and let your component decide if the route should be changed. Something like this:
// todo-action.js
class TodoActions extends Actions {
createMessage(todo) {
return TodoStore.saveToServer(todo);
}
}
// todo-list.js
const TodoList extends React.Component {
render() {
...
}
addTodo(todo) {
this.props.flux.addTodo(todo).then(response => {
if (response.some.prop === someValue) {
this.props.router.transitionTo(...);
}
});
}
}
That way, the action is still nicely decoupled from the route change. If you want to do the route switch in more than one place, you could encapsulate that in a addTodoAndSwitchRoute method in your Flux class.

Resources