Passing props.children from a container component to a presentational component - reactjs

I'm doing some routing, I want to try to display IndexZoomOverviewContainer into IndexZoomViewPanelContainer.
When I go to the right path "...index/overview", IndexZoomViewPanelContainer is displayed, but when I'm passing the children (the Route for IndexZoomOverviewContainer in this case) from the container (IndexZoomViewPanelContainer) to the view (IndexZoomViewPanelComponent), it doesn't display it and gives me an error:
Error ScreenShot : https://i.gyazo.com/990f92d3058806baa576dca5247ace9e.png
When I removed this.props.children, its not showing any error.
Here is the routing:
<Route className="fullHeight fullWidth" key="indexzoom" path="index/" component={indexmonitor.IndexZoomViewPanelContainer} >
<Route className="fullHeight fullWidth" key="indexzoom1" path="overview" component={indexmonitor.IndexZoomOverviewContainer} />
<Route className="fullHeight fullWidth" key="indexzoom2" path={routes.INDEX_ZOOM_CONSTITUENTS_RELATIVE_PATH} component={dashboard.DashboardListContainer} />
</Route>
IndexZoomViewPanelContainer:
class IndexZoomViewPanelContainer extends React.Component {
constructor(props) {
super(props)
}
componentDidMount() {
}
componentWillUnmount() {
}
render() {
return <IndexZoomViewPanelComponent>
{this.props.children}
</IndexZoomViewPanelComponent>;
}
}
IndexZoomViewPanelComponent:
function IndexZoomViewPanelComponent(props) {
const tabs = getTabs();
return (
<div className="container">
<viewPanel.ViewPanel title={"Index Zoom"}
authKey={perm.INDEX_ZOOM_VIEWPANEL_PERM}
path={route.APP_PATH}
getPermStateFunc={(state) => state.MENUPERMS}
>
<TabControl tabs={tabs} selected={route.INDEX_ZOOM_OVERVIEW_RELATIVE_PATH}>
{props.children}
</TabControl>
</viewPanel.ViewPanel>
</div>
);
}

I want to try to display IndexZoomOverviewContainer into IndexZoomViewPanelContainer
<Route className="fullHeight fullWidth" key="indexzoom" path="index/"
component={indexmonitor.IndexZoomViewPanelContainer} >
IndexZoomViewPanelContainer renderd as a part of routing and it don't have any children
So,
return <IndexZoomViewPanelComponent>{this.props.children}</IndexZoomViewPanelComponent>
in the above script, this.props.children will be undefined and it will throw the error that you have shared.
In order to work this,it should be,
return <IndexZoomViewPanelComponent><IndexZoomOverviewContainer /></IndexZoomViewPanelComponent>

Here's a way to achieve what you need in the route :
// I'm renaming your components and removing className/key for readability
<Route path="/index" render={() => (
<IndexZoomViewPanelContainer>
<Route path="/index/overview" component={IndexZoomOverviewContainer} />
</IndexZoomViewPanelContainer>
)}
/>
You can also make your nested routes in IndexZoomViewPanelContainer component directly.
See https://reacttraining.com/react-router/core/api/Route/render-func for more infos.

Related

react-router-dom v6: What would be the equivalent to Route's render prop?

In my App.js file, I was previously able to render a dynamic route using the following approach:
<Route exact path="/episodes/:id" render={(props) => {
const episodeNumber = props.location.pathname.replace('/episodes/', '');
return (
<EpisodeDetails episode={this.state.episodes[episodeNumber]} />
);
}}/>
EpisodeDetails.js
const EpisodeDetails = (props) => {
return (
<div className="Episode">
<h2><span>Episode {props.episode.id}: </span>{props.episode.title}</h2>
<p>{props.episode.description}</p>
<audio controls>
<source src={props.episode.source} type="audio/mp3" />
</audio>
</div>
);
};
export default EpisodeDetails;
But as of V6, this approach no longer works. The URL pathname updated correctly (ie, /episodes/2), but the component isn't rendered for the path.
What would be the V6 equivalent to what I'm trying to accomplish? The documentation doesn't make this very clear.
It's odd you declared a route match param in your v5 code for the episode number and then didn't use it. In react-router-dom v6 you can create a wrapper component to "sip" the id match param and pass along the specified episode number.
const EpisodeWrapper = ({ episodes }) => {
const { id } = useParams();
<EpisodeDetails episode={episodes[id]} />
}
...
<Route
path="/episodes/:id"
element={<EpisodeWrapper episodes={this.state.episodes} />}
/>

how to route to a different page in react?

I want to redirect to two different pages on login by the user. Can anyone explain how I can do so in the following code? I am using aws cognito for authentication. if the group of the user is employee it should redirect to a different page.
I am currently unable to access the cognito group outside Auth.currentSession() function.
const Login= () => {
Auth.currentSession().then(res=>{
let cognitoGroups = res.accessToken.payload["cognito:groups"]
console.log(`group: ${cognitoGroups}`)
if(cognitoGroups[0]==="Employee")
console.log('employee!');
});
return(
<div>
{cognitoGroups=="Employee"} ?
<Route path='/employee' component={Emlogin} />
<Redirect from="login" to="employee" />
:
<Route path='/manager' component={Mglogin} />
<Redirect from="login" to="manager" />
<AmplifySignOut />
</div>
)
}
export default Login;
You are using the {... ? ... : ...} incorrectly, in the jsx part your condition needs to be something like this
let cognitoGroups = "Employee"
return(
<div>
{cognitoGroups ?
(<Route path='/employee' component={Emlogin} />
<Redirect from="login" to="employee" />)
:
(<Route path='/manager' component={Mglogin} />
<Redirect from="login" to="manager" />
<AmplifySignOut />)}
</div>)
Also, if you want to check for the type of user, you can add a condition like {cognitoGroups == "employees" ? doThis() : elseDoThat()}

React component redirect with onClick of table row that already has another onClick

I have a table row that already has an onClick, which is used to send data to a api in order to render new content.. Presently I have that api rendering to the landing page. I would however like the data to render to a separate page/pathway.
Do I want to use Redirect, Link, another onClick, etc.. Just confused on where to start and the best method to use.
const SearchResults = props => (
<tbody>
{ props.contracts.map((contract, i) =>
<tr key={i} data-id={contract.Id}
onClick={() => props.handleContract(contract.Fields.filter(field => field.DataField==="IDXT001").map(field => field.DataValue))}
>{contract.Fields.map( docs =>
<td key={docs.Id}><span id={docs.DataField}>{docs.DataValue}</span></td>)}
</tr> )}
</tbody>
)
Here is my main class page. I would like the Pdfs to load on a new page.
<SearchResults
labels={this.state.labels}
contracts={this.state.contracts}
pdfs={this.state.pdfs}
handleContract={this.onClick}
/>
<Pdfs
labels={this.state.labels}
contracts={this.state.contracts}
pdfs={this.state.pdfs}
/>
Here is my App.js
class App extends Component {
render() {
return (
<div className="App">
<BrowserRouter>
<div>
<Header />
<Route exact path="/" component={Search} />
<Route path="/pdfs" component={Pdf} />
<Footer />
</div>
</BrowserRouter>
</div>
);
}
}
In your method you could use:
this.props.history.push({
pathname: '/yourpath',
state: { labels={this.state.labels}
contracts={this.state.contracts}
pdfs={this.state.pdfs} }
})
You can modify your onClick function to do some calculation on where you need to go based on where you are in the router history

React router 4 - detect if one of child route matches in a parent component

I am using react router 4 to display a list of users. The component Users loads when the route matches \Users. Inside that, I have a Route defined (user details) that's loaded when the URL matches \Users\:id.
The Users page has a grid/table with few columns.
render(){
const {users, flags} = this.props;
return (
<div>
<h3>Users</h3>
<div className="col-md-12">
<UsersGrid users={this.state.users} />
</div>
<Route path="/users/:id" component={UserDetail} />
</div>
)
}
What I want is if the user details route is active then to shrink the user grid (say apply class col-md-4) and show the user detail to its right.
But I am not sure how can I detect if a child route is active so that I can conditionally apply a class to an element. I tried using this.props.children but it is always undefined.
You would make use of matchPath from react-router
import { matchPath } from 'react-router'
render(){
const {users, flags, location } = this.props;
const match = matchPath(location.pathname, {
path: '/users/:id',
exact: false,
strict: false
})
return (
<div>
<h3>Users</h3>
<div className="col-md-12">
<UsersGrid users={this.state.users} />
</div>
<Route path="/users/:id" component={UserDetail} />
</div>
)
}
If url shows in address bar then you can check with window.location if it contains string like id after /user then perform action with condition check:
if(/user/{anyid}) {
*your condition action*
} else {
*else action*
}
you can use string regex to match the id.

Trying to make a sub page (channel) in reactjs

I am trying to make the user able to create channels in my web app.
If you the see image below, there is a modal that take the input and use
that as the id of the page that will newly created.
My route is specified as:
require('./styles/main.scss');
render(
<Router>
<div>
<Route exact path="/" component={Home}/>
<Route exact path="/RecTest" component={RecTest}/>
<Route exact path="/Example" component={Example}/>
<Route path="/channels/:id" component={ (props) => (<Channel {...props}/>)} />
</div>
</Router>,
// Define your router and replace <Home /> with it!
document.getElementById('app')
);
And the modal is coded as:
class MakeChannelModal extends Component {
constructor(props){
super(props);
console.log("constructor");
this.state={
channelName: ''
}
this.clickHandler = this.clickHandler.bind(this);
this.inputChangeHandler = this.inputChangeHandler.bind(this);
}
inputChangeHandler(event) {
this.setState({
channelName: event.target.value
})
}
clickHandler() {
<Link to={{ pathname: '/channels/' + this.state.channelName }}></Link>
}
render(){
return(
<div className="ui active modal">
<div className="header">Create a New Channel</div>
<div className="loginbody">
<div className="fillout">
<div className="channelName">
<span>Channel Name: </span>
<Input
onChange = { this.inputChangeHandler }
placeholder="Channel Name"
label = "Channel Name"
value = { this.state.channelName } />
</div>
</div>
<Button onClick = { this.clickHandler }>
SUBMIT
</Button>
</div>
</div>
)
}
}
export default MakeChannelModal
I was assuming this to take the channel name input and direct the page to a Channel component with the id of df.
My Channel component is just blank right now.
The thing is, if I click SUBMIT in the modal, it does not do anything.
Doesn't even show an error.
What am I doing wrong?
clickHandler() should be a function that changes pathname, making Router to draw another component, but it tries to draw React component by itself instead (not quite successfully though, because this function doesn't actually returns anything).
I believe you should either use props.history.push() in onClick(), or make <Button /> a <Link />.
Solution 1: (preferable, if you want a <Button>)
Replace your clickHandler() with this:
clickHandler() {
this.props.history.push('/channels/' + this.state.channelName);
}
Solution 2: (still would work)
Replace your <Button ... /> component with this:
<Link to={{ pathname: '/channels/' + this.state.channelName }}>
SUBMIT
</Link>
SO answer about history.push(): https://stackoverflow.com/a/43298961/6376451

Resources