React-router and redux manual URL enter error - reactjs

i have a problem of undefined props from redux store.
here is my routeHandler file
function organisationsFromStore(store) {
const { router, organisations } = store;
return {
organisations
}
}
function organisationFromStore(store) {
const { router, organisations } = store;
const { organisationId } = router.params;
return {
organisation: organisations.get(parseInt(organisationId))
}
}
export const organisationRouteHandler = connect(organisationFromStore)(Organisation);
export const accountsConfigurationRouteHandler = connect(organisationsFromStore)(AccountsConfiguration);
This is hooked to my getRoutes.jsx file which handles routes:
<Route path="accounts" component={accountsConfigurationRouteHandler}>
<Route path=":organisationId" component={organisationRouteHandler}></Route>
</Route>
In my Organisation.jsx(which gets organisations prop from it's parent AccountsConfiguration) component i have:
render() {
return (
<div style={{display: "inline"}}>
<div className={styles.desks}>
<span className={deskStyles.desksLabel}>Desks</span>
<ul className={deskStyles.desksList}>
{
this.props.organisation.get("desks").map(desk => {
return <li>{desk.get("name")}</li>;
})
}
</ul>
</div>
<div className={this.state.childrenStyle}>
{this.props.children}
</div>
</div>
);
When i thy to enter my URL manually e.g localhost:1234/accounts/4 (4 being the organisationId), i get the error saying this.props.organisations is not defined, which breaks the app. This is happening because first route's handler (organisationsFromStore) did not store the organisations and it didn't pass it as prop to AccountsConfiguration, which then didn't pass it to Organisations via this.props.children.
What is the best way to make the component wait for all the previous routes to get their props, and then render in this case Organisation component without error? Or is there a better way of doing this. Hope i was clear, thanks.
P.S I'm using old redux-router version before v2, and it must be that version at this time.

Ok, my mind wasn't working properly yesterday, i just inserted check if props are defined and rendered it conditionaly this.props.organisation ? .... : null

Related

How to open a new page and pass data between them in React JS

I am upgrading the searchbar of my project. I would like that when I press on one of the result a new page is opened that contains all the data of the selected object. Here is the code of the function which show the result of the search in a Link, and in theory when I press on a name I should go to the page passing the object to the new page.
{
filteredData.length != 0 && (
<div className="dataResult">
{filteredData.slice(0, 3).map((obj, key) => {
return (
<Link
className="dataItem"
to={{ pathname: "/SelectedObj", state: obj }}
>
{obj.Name}
</Link>
);
})}
</div>
);
}
So this part of code works, but the new page result without nothing, and i can not understand how to access to the object I have passed to the new page. Here the code of the new page.
function SelectedUser() {
return <h1>Hello world</h1>;
}
The thing I don't understand is why it doesn't show me Hello world, other than how to access the passed object.
EDIT: I checked and I forgot to update the routing part. Now everything works, thanks.
From Parent to Child Using Props
Try to imagine the directory structure of the app as follows: the parent component actually renders the child components in the app.
App
└── Parent
├── Child1
└── Child2
This is the simplest and most basic direction of data flow in React.
class Parent extends React.Component {state = { data : "Hello World" }
render() {
return (
<div>
<Child1/> //no data to send
<Child2 dataFromParent = {this.state.data} />
</div>
); }
}
Use the variable this.props.dataFromParent to obtain the data passed from parent to child.
class Child2 extends React.Component {
render() {
return (
<div>
Data from parent is:{this.props.dataFromParent}
</div>
);
}
}
From Child to Parent Using Callbacks
Say you want to send a message from child1 to parent with the message "Hello, how are you?" Take the following steps:
In the Parent.js, set a callback function to take in the parameter that you have accessed from the child.
Send the callback function to the child1.js as a prop.
class Parent extends React.Component {
state = { message: "" }
callbackFunction = (childData) => {
this.setState({message: childData})
},
render() {
return (
<div>
<Child1 parentCallback = {this.callbackFunction}/>
<p> {this.state.message} </p>
</div>
);
}
}
Pass your data using this.props.callback(dataToParent) in the child1.js.
class Child1 extends React.Component{sendData = () => {
this.props.parentCallback("Hey Popsie, How’s it going?");
},
render() {
//Any time you wish to send data from child to parent component, call the sendData function.
}
};
for the link
<Link to={"dataitem"}
state={{ state : "value" }}
>Navigate</Link>
and in the dataitem page use the useLocation to get the data like this
import { useLocation } from "react-router-dom";
const location = useLocation();
console.log(location.state);
To access the object passed to the new page,
use useLocation hook which is a function that returns the location object that contains information about the current URL. Whenever the URL changes, a new location object will be returned.
import { useLocation } from "react-router-dom";
function SelectedUser() {
const location = useLocation();
console.log(location.state);
return (
<h1>Hello world</h1>
)
}

react-router-dom v6 useNavigate passing value to another component

The version of react-router-dom is v6 and I'm having trouble with passing values to another component using Navigate.
I want to pass selected rows to another page called Report. But, I'm not sure I'm using the right syntax for navigate method and I don't know how to get that state in the Report component.
Material-ui Table: I'm trying to use redirectToReport(rowData) in onClick parameter.
function TableRows(props){
return (
<MaterialTable
title="Leads"
columns={[
...
]}
data = {props.leads}
options={{
selection: true,
filtering: true,
sorting: true
}}
actions = {[{
position: "toolbarOnSelect",
tooltip: 'Generate a report based on selected leads.',
icon: 'addchart',
onClick: (event, rowData) => {
console.log("Row Data: " , rowData)
props.redirect(rowData)
}
}]}
/>
)}
LeadTable component
export default function LeadTable(props) {
let navigate = useNavigate();
const [leads, setLeads] = useState([]);
const [loading, setLoading] = useState(true);
async function fetchUrl(url) {
const response = await fetch(url);
const json = await response.json();
setLeads(json[0]);
setLoading(false);
}
useEffect(() => {
fetchUrl("http://localhost:5000/api/leads");
}, []);
function redirectToReport(rowData) {
navigate('/app/report', { state: rowData }); // ??? I'm not sure if this is the right way
}
return(
<div>
<TableRows leads={leads} redirect={redirectToReport}></TableRows>
</div>
)}
Report component
export default function ReportPage(state) {
return (
<div>
{ console.log(state) // This doesn't show anything. How to use the state that were passed from Table component here?}
<div className = "Top3">
<h3>Top 3 Leads</h3>
<ReportTop3 leads={[]} />
</div>
</div>
);}
version 6 react-router-dom
I know the question got answered but I feel this might be helpful example for those who want to use functional components and they are in search of passing data between components using react-router-dom v6.
Let's suppose we have two functional components, first component A, second component B. The component A wants to share data to component B.
usage of hooks: (useLocation,useNavigate)
import {Link, useNavigate} from 'react-router-dom';
function ComponentA(props) {
const navigate = useNavigate();
const toComponentB=()=>{
navigate('/componentB',{state:{id:1,name:'sabaoon'}});
}
return (
<>
<div> <a onClick={()=>{toComponentB()}}>Component B<a/></div>
</>
);
}
export default ComponentA;
Now we will get the data in Component B.
import {useLocation} from 'react-router-dom';
function ComponentB() {
const location = useLocation();
return (
<>
<div>{location.state.name}</div>
</>
)
}
export default ComponentB;
Note: you can use HOC if you are using class components as hooks won't work in class components.
Your navigate('/app/report', { state: rowData }); looks correct to me.
react-router-v6
If you need state, use navigate('success', { state }).
navigate
interface NavigateFunction {
(
to: To,
options?: { replace?: boolean; state?: any }
): void;
(delta: number): void;
}
Your ReportPage needs to be rendered under the same Router that the component doing the push is under.
Route props are no longer passed to rendered components, as they are now passed as JSX literals. To access route state it must be done so via the useLocation hook.
function ReportPage(props) {
const { state } = useLocation();
console.log(state);
return (
<div>
<div className="Top3">
<h3>Top 3 Leads</h3>
<ReportTop3 leads={[]} />
</div>
</div>
);
}
If the component isn't able to use React hooks then you still access the route state via a custom withRouter Higher Order Component. Here's an example simple withRouter HOC to pass the location as a prop.
import { useLocation, /* other hooks */ } from 'react-router-dom';
const withRouter = WrappedComponent => props => {
const location = useLocation();
// other hooks
return (
<WrappedComponent
{...props}
{...{ location, /* other hooks */ }}
/>
);
};
Then access via props as was done in pre-RRDv6.
class ReportPage extends Component {
...
render() {
console.log(this.props.location.state);
return (
<div>
<div className="Top3">
<h3>Top 3 Leads</h3>
<ReportTop3 leads={[]} />
</div>
</div>
);
}
}
2 things (just a suggestion):
Rather than a ternary use &&
{location && <div>{location.state.name}</div>}
Why are you checking location and rendering location.state.name? I would use the check on the data you are fetching or make sure the data returns null or your value.
On Sabaoon Bedar's Answer, you can check if there is any data or not before showing it :
Instead of this <div>{location.state.name}</div>
Do this { location != null ? <div>{location.state.name}</div> : ""}
if you want to send data with usenavigate in functional component you can use like that
navigate(`/take-quiz/${id}`, { state: { quiz } });
and you can get it with uselocation hook like this
const location = useLocation();
location.state.quiz there is your data
But you cannot get this data in props it;s tricky part ;)!!
on SABAOON BEDAR answer,
from component A: navigate('/', {state:"whatever"}
in component B: console.log(location.state) //output = whatever

Unable to prevent rerender with shouldComponentUpdate() or with PureComponent

In /home/:id I have a <LogButtons/> when its clicked logOn() get called so logsignPopUp variable become a <logForm/> component.
In the same page I have a <IframeYoutubeComponent/>, I want to prevent it to rerender when the <logForm/> pop on the screen so the video isn't reloaded.
home.js :
export default class Home extends Component {
constructor(props) {
super(props);
this.state = { logsign: "" };
this.logOn = this.logOn.bind(this);
this.signOn = this.signOn.bind(this);
}
logOn() {
this.setState({ logsign: "log" });
}
render() {
let logsignPopUp = this.props.logsign === "log" ? <LogForm/> : this.state.logsign;
let homePage =
<div>
{logsignPopUp}
<div>
<LogButtons logOn={this.logOn}/>
</div>
<div>
<IframeYoutubeComponent paramsId={this.props.match.params.paramsId}/>
</div>
</div>;
return (
<div>
<Route exact path="/home/:id" component={() => <div>{homePage}</div> } />
</div>
);
}
}
iframeYoutubeComponent.js :
export class IframYoutubeComponent extends Component {
render() {
//this.props.youtube come from Redux state
let src = this.props.youtube.find(el => el.youtubeId === this.props.paramsId);
return (
<iframe src={"https://www.youtube.com/embed/" + src}></iframe>
);
}
}
I tried to return false in shouldComponentUpdate() but its not even called :
shouldComponentUpdate() {
console.log("test");
return false;
}
I tried to use a PureComponent for <IframeYoutubeComponent/> but the video still reload when the <logForm/> pop.
I tried to add key to my components and also tried to put this.state.logsign in Redux but nothing worked out.
I started react since 2 months so I might miss something obvious but I can't find out what... Any idea ?
That's because you are passing an arrow function in a component prop to the Route. This way everytime you generate a new function.
You should pass a react component in this prop or at least a function that returns JSX but this function should be defined once. For example as a class method.
Something like:
<div>
<Route exact path="/home/:id" component={this.renderHomePage} />
</div>
But then of course you have to refactor your logic regarding this logsign prop.

Can the url params of the react-router <Route /> component be validated?

I'm using react-router (2.4.1) and I have a wildcard (path="*") fallback route to display a "Not Found" component.
One of my routes are defined like:
<Route path="/campaigns/:category" component={CampaignsPage} />
Can I validate the category route param in any way to check if this category exist in my list of categories? and if not, I want the router to fall down to the wildcard in order to display the Not Found component
Maybe you could validate the received param inside the 'campaigns' component? Something along the lines of:
class CampaignsPage extends Component {
render() {
const validCampaignCats = [...]
const receivedCampaignCat = this.props.params.category
const renderChild = (actualCat, validCats) => {
if (!validCats.includes(actualCat)) {
return <YourErrorPage/>
}
return this.props.children
}
return (
<div>
<Header/>
{ renderChild(receivedCampaignCat, validCampaignCats) }
<Footer/>
</div>
)
}
}
Hope that helps!

Get the current route name into the component

I am trying not to use : if(window.location) to get access to my current route.
I am working on a code where the router is already defined, and the component is already written. I am trying to access the router information from the component because I need to apply a classname depending on the route.
Here's how the router looks like:
export const createRoutes = (dispatch, getState) => (
<Route component={AppLayout} >
<Route path="/" component={Home} onEnter={(nextState, replaceState) => {
console.log('on Enter / = ', nextState);
nextState.test = 'aaaa'; //can't seem to pass states here....
}} />
then in my component:
render() {
//I want to access my super router or location . in fact
// I just want to know if currentURl === '/'
//and after reading the react-router doc 5 times - my head hurts
// but I still hope to get an answer here before putting a window.location
}
`
Depending on which version of React Router you're using you have access to different objects available on the context of a component.
MyComponent.contextTypes = {
location: React.PropTypes.object
}
which will enable you to do the following
render() {
if (this.context.location.pathname === '/') {
return <h1>Index</h1>;
} else {
return <h2>Not index</h2>;
}
}

Resources