how to route to a different page in react? - reactjs

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()}

Related

React hooks : 'Cannot read property 'push' of undefined'

I'm trying to redirect my homepage to "/call" page based on a redux state. I can go to that component by typing the url manually but cant do it with a function. I tried "Redirect to", "history.push" but none of them worked for me. I cant solve the problem. Here is my code;
const Phone = ({ hidden, photoOpened, inCall }) => {
const dispatch = useDispatch(getContacts());
let history = useHistory();
useEffect(() => {
if (inCall.inCall) {
history.push('/call')
}
}, [inCall]);
useEffect(() => {
dispatch(getContacts());
}, [])
return (
<div hidden={process.env.NODE_ENV === 'development' ? !hidden : hidden} className={photoOpened ? "phone-container-rotate" : "phone-container"}>
<div className="coque" />
<Suspense fallback={<div className="animated fadeIn pt-1 text-center">Loading...</div>}>
<HashRouter basename="/phone">
<div
className="phone-content"
style={{ backgroundImage: `url(${background})` }}
>
<HeaderBar />
<BannerNotifications />
<Switch>
{routes.map((route, idx) => {
return route.component ? (
<Route
key={idx}
path={route.path}
exact={route.exact}
render={props => <route.component {...props} />}
/>
) : null;
})}
</Switch>
</div>
<Route component={BottomPhoneNavigator} />
</HashRouter>
</Suspense>
</div>
);
};
You could try and test for history existence of the history in your effect, also add it to dependency list
useEffect(() => {
if (history && inCall.inCall) {
history.push('/call')
}
}, [inCall, history]);
And important thing, your component using this hook must be within the Router, I see you'\re using HashRouter but as child of component using the hook.
Also if you're stuck to this structure, why wont you try to use Redirect within the Switch? This could work with some smart test so you wont end up in a loop:)
To use history your Phone component should be inside router component

useState hook being called inside map function causing infinite loop

I'm currently building a dynamic form engine and I want to display results from the redux store when the Answer Summary component is rendered. The way I figured would be best to do this would be to having a 'complete' status and set it to true once the answerSummary component is loaded, but doing this within the map function does not work and throws the infinite loop react error.
Code is here:
function App() {
let [complete, setComplete] = useState(false);
return (
<div>
<h1>Form App Prototype</h1>
<Router>
<Switch>
{Object.values(Database.steps).map(step => {
step.name === 'answerSummary' ? setComplete(true) : setComplete(false);
return (
<Route exact path={`/${step.name}`} render={() =>
<Step step={step} />
}
/>
)
})}
</Switch>
</Router>
<br></br>
<div style={{display: complete? 'block' : 'none'}}><StoreVisual/></div>
</div>
);
}
export default App;
EDIT: I know you aren't able to setState inside the render - I've written it this way as a way to try and convey what I want to be able to do
My understanding of your problem is that you are trying to display results after the answer summary component is mounted.
You can achieve this by using the useEffect hook which runs when the component mounts. https://reactjs.org/docs/hooks-effect.html
If you only want to render <StoreVisual/> when they are on the last step it might be easier to set up a state hook for the index of the step people are on.
const [index, setIndex] = useState(0);
Every time someone progresses a step increment this value.
You would have to pass setIndex, or whatever you call your setter, into the Step component to do this.
Then you can render <StoreVisual/> with a conditional, like so...
<div>
<h1>Claimer Form App Prototype</h1>
<Router>
<Switch>
{Object.values(Database.steps).map(step =>
<Route exact
path={`/${step.name}`}
render={() => <Step step={step} /> }/> )}
</Switch>
</Router>
<br></br>
{Database.steps[index] === 'answerSummary' && <StoreVisual/>}
</div>
This approach also affords you a simple way to let people start in the middle of the form. Say you want to let people save half-finished forms in the future, you just change/update the default value of index hook.
Instead of running that code inline in your return, build the array in your function logic:
function App() {
let [complete, setComplete] = useState(false);
// build an array of Route components before rendering
// you should also add a unique key prop to each Route element
const routes = Object.values(Database.steps).map(step => {
step.name === 'answerSummary' ? setComplete(true) : setComplete(false);
return <Route exact path={`/${step.name}`} render={() => <Step step={step} />} />
})
return (
<div>
<h1>Claimer Form App Prototype</h1>
<Router>
<Switch>
// render the array
{[routes]}
</Switch>
</Router>
<br></br>
<div style={{display: complete? 'block' : 'none'}}><StoreVisual/></div>
</div>
);
}
export default App;
I don't think you need to call setComplete(false) based on this logic, so you should probably replace your ternary with an if statement:
if (step.name === 'answerSummary') {
setComplete(true)
}
and avoid making unnecessary function calls
You cannot set state inside render which cause infinite loop.[Whenever state is changed, component will re-render(calls render function)]
render()=>setState()=>render()=>setState().......infinite
WorkAround:
<div style={{display: this.props.location.pathname=='answerSummary'? 'block' : 'none'}}><StoreVisual/></div>

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

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

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.

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