Is there any option to change route and pass props to another component in Next.js? - reactjs

I am at this path: http://localhost:3034/dashboard/one.
Now I want to pass a large object in another component <Edit/> that has an object and also change on the path to http://localhost:3034/dashboard/edit.
I don't want to use the next router to pass all objects with queries in URL. That one way, I know.
Example: router.push("/dashboard/edit/" + object)
I have data that pass from this slug: http://localhost:3034/dashboard/one to http://localhost:3034/dashboard/edit.

If you don't want to use the router to pass your data, a few options are left.
using a Reducer: handy and powerful, but lots of boilerplate to get it working
using a Context: powerful as well, a little tricky to master at first, and can trigger lots of redraws if not used properly
using the local storage: very easy to setup, but requires your data to be serializable, You can use libraries such as react-use to make your life easier
refetch your item in your edit page using an id: you could have your url like /dashboard/one/edit/ and fetch the data directly from that page instead of carrying the object around

Related

Why is using React Context better than just passing around a props object?

I've been reading about the advantages of using Context in React and I am unconvinced. I'm wondering if there's something I've missed.
Context provides a way to pass data through the component tree without having to pass props down manually at every level.
What's the hassle in creating a props object in the main component and just passing it around among the underlings? Something like:
// do this once at top level (I'm assuming [foo, foo_set] and [bar, bar_set] are state variables):
const props = {foo, foo_set, bar, bar_set, thisAndThat, theOther, whatever, etcAndEtc}
// including one component
<MyComponent1 {...props } />
// including another
<MyComponent2 {...props } />
(Maybe better to use another name than props for this object, as the components can have other properties. Anyway.)
Then in MyComponent1 you can access all the props you want, or not access them. Either:
...
const MyComponent1 = (props) => {
...
// here we can use any props we need, props.bar, props.bar_set, props.theOther for example
const localVar = props.bar * 2;
props.bar_set(localVar);
// this changes the value of bar throughout the app
...
}
the advantage of the above, as I see it, is that you can pass around the props object to other sub-sub-components and not worry about whether you have anything missing.
Or:
...
const MyComponent1 = ({bar, bar_set, theOther }) => {
...
// here we can use bar, bar_set, theOther in the same example
const localVar = bar * 2;
bar_set(localVar);
...
}
The advantage of this option being that the syntax is shorter.
So my point is why not just use the standard JavaScript syntax? Why introduce new concepts when there are plenty to assimilate to do all sorts of other things?
Consider a fairly common case for most applications: You have authentication information (eg, the current user), a routing library (eg, react-router), and a theme object (what colors to use). These are needed in components scattered throughout the app.
You want to render a button somewhere down at the tip of the component tree. It's going to show the user's avatar, so it needs the authentication data. It's going to navigate when clicked, so it needs the navigate function from the routing library. And it needs to style itself according to the theme.
This certainly can be done through props, but in order for the button to get the props, every component in the chain above it must get and forward those props too. This could be many components deep, like page component -> section component -> table -> row -> widget -> button, and most of them don't need that information for themselves, so they're just taking the props in order to forward it along.
And you can easily imagine cases where there are more than 3 pieces of data that are needed across the app.
What's the hassle
Most people find this "prop drilling" to be a hassle, but let's assume you don't. You still have the problem that it has bad performance. If every component must receive the full set of "global" values that the app might need, then any time anything changes, the entire app must rerender. Optimizations like react.memo become effectively useless. You will get much better performance if you only pass the props you need.
Easier to edit code (You don't have to delete for example unused variable)
Better redability (You dont see unnescesary variables, and You see which component is using variables)
Lesser performance waste (preventing from consuming unnescesarry variables)
Suppose You got 10 descendants in - You would have to pass one variable through 10 of components.
What if some could have the same variable name ? You would have to edit Your passed variable for a while, then edit back later.
To sum up:
Using Context more efficient than stuffing everything into a single object variable, because it avoids re-rendering the whole app when anything changes.
People think passing a single variable around is more hassle than introducing specific syntax.
Context also allows you to have different values for the same variable in different parts of the app. This is shown here (the best explanation IMHO) : https://beta.reactjs.org/learn/passing-data-deeply-with-context
The above article also specifies that sometimes passing props is the best solution. It gives a list of use cases for context, and the advantages provided in each case.

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.

How to update match.params?

The react app has search page. There are input.
The path is 'search/:query', and by default you see zero results.
If you go to 'search/star%20wars' you will see some results. In componentDidMount() I added if statement to load result if match.params.query is not null.
If I type into search input Spider Man and click submit - I trigger a search and show results. But if you reload page - you will see the result about Star Wars. So how update match.params.query? Or may be there other solution of fix this.
You need to update the history object as well.
What you are doing is altering the history object available to you and calculating the results based on that object. But when you will refresh the page it still holds the original history object.
One way of doing it, you need to push or replace a new route in the history.
Because evert search page is a new page, so if you want the previous pages to stay preserved you should use history.push otherwise history.replace
Implement it like this:
var routeObj = {
pathname: samePath,
state: sameState,
query: newQuery
}
//push it in your history using which ever routing library you are using.
//For Example:
router.history.replace(routeObj);
Note: Do not worry about rendering speed on changing the history. React is smart enough to handle that. Basically whenever you will push a route whose component is already mounted it will not unmount and remount the same component again, rather it will just change the props and will re render it.
The callback for this case will be => componentWillReceiveProps
#misha-from-lviv The way I see your problem statement is that you have two source of truth on is the query params, using which you should update your state, and the other is the default state which is populated from the default value of your filters.
As #Akash Bhandwalkar suggested, you do need to update the route in using the History API. But also you also a need a top-level orchestrator for your application state, which will allow you to read and write to the history api ( change your route ) and also do an XHR / fetch for you to get the results.
How I'd approach this is that I'd start with a Parent component, namely FiltersContainer , which actually does this orchestration to read and write to the url. This Container would have all the side-effect knowledge for fetching and updating the routes ( error handling included ). Now the all the child components ( filters and search results maybe ) will just read the state thus orchestrated and re-render.
Hope this guides your thinking. Do revert here if you need further guidance. 😇
Cheers! 🍻

React + Alt: Use data from multiple stores to make a request to DataSource

I am working on an app using React with Alt flux implementation.
Currently, I'm trying to figure out the best way to use data from multiple Alt stores to compose a request to the backend.
Say, I have a SheetDataStore, FiltersStore and a fetchFilteredData method in my DataSource file. To get proper data from backend, i need to pass to fetchFilteredData both some sheet data and filters, but when you call the fetchFilteredData method from one of the stores, you can pass some arguments and the state of just that exact store.
So i see 2 ways of handling this case:
1) I make 2 actions: prepareFilters and fetchData. The prepareFilters is called from a component and gets handled by FilterStore, which prepares all required data and after that calls fetchData action, passing the prepared data as an argument. The SheetDataStore handles the fetchData action and calls the fetchFilteredData having all required data now.
What i don't like here is that it seems to me, Stores should not be calling actions, so that's kind of a hacky solution.
2) I import FilterStore to the SheetDataStore and use FilterStore.getState() inside of one of SheetDataStore methods to get all the data i need. That seems easy, but there might be some pitfalls of coupling the stores like that.
Are there any best practices for such a case? Maybe some of you faced similar issue and can recommend which of the paths to take from your experience?
Do the binding in the component. Say you have FilterComponent then pass the search action SheetDataAction.search to it:
<FilterComponent search={SheetDataAction.search} />
And in the FilterComponent.render() do something like <button onClick={() => this.props.search(this.props.criteria)} />

Relationship between list and item with relational data store (using react-redux)

I'm using React with Redux as my store. I'm also using the react-redux library to integrate the two. My store contains two sets of data:
Task { id, name, assigneeId }
User { id, name }
I have a TaskListComponent which (using react-redux) connect-s to my store using:
#connect(state => {
tasks: state.tasks,
users: state.users
})
I need both because the list has a filter allowing for searching my task name or user name - my component needs to be 'user aware'.
This component lists out another component called TaskItemComponent which displays the task name and the name of the assigned user.
I'm struggling to decide on the best approach for resolving a task's assigned user.
React guidelines tell me that the Item component should take a Task as a prop, and resolve the User itself in the render function. However, this requires the component to be store-aware and, using the react-redux library, it doesn't appear to be designed to allow a component to access the store without being connect-ed to it.
Or I could resolve the User in the list and pass it to the Item component along with the task, e.g. <TaskItemComponent task={task} assignee={resolveTaskAssignee(task)} />. This has the benefit of keeping my Item 'dumb', and means I don't need to have it listening to store changes (or even know about the store).
Any advice would be appreciated.
Both approaches are fine. I'd start with passing props from outside, and once it gets tedious, let the Task component receive task as a prop but read its user by using connect.
See also https://stackoverflow.com/a/25701169/458193.
There's nothing inherently wrong with option #2. You have to do data ops somewhere. It will always be the right place to do those ops in the smart component.
Personally, I'd prefer a better data model in the store. But, you got what you got.

Resources