I've been trying solving this and read everything.
Below I have a successful request so I want to go to home page. It does go to home page but then redirects back. Here is a video https://monosnap.com/file/QOB2uIKXDrLpiVLsztxN9oAHSBuiY5. Any ideas? Thank you for your help.
import { withRouter } from 'react-router-dom'
export default class New extends Component {
createRequest(params) {
if (Object.values(this.state.validations).includes(false)) { return }
this.deliveriesService.create(params)
.then((res) => {
if (res.ok) {
this.props.history.push("/");
}
})
}
}
My Home Page looks like this:
import React, { Component } from "react";
import deliveriesService from "./services/deliveriesService.js";
import { withRouter } from 'react-router-dom'
class Home extends Component {
constructor() {
super()
this.state = {
deliveries: []
};
}
deliveries() {
this.deliveriesService.index()
.then((res) => {
this.setState({
deliveries: res,
});
})
}
render() {
const deliveries = this.state.deliveries;
return (
);
}
}
Here is my routes
import React from 'react'
import { Switch, Route } from 'react-router-dom'
import Home from "./Home";
import New from "./New";
const Main = () => (
<main>
<Switch>
<Route exact path='/' component={Home}/>
<Route exact path='/new' component={New}/>
</Switch>
</main>
)
export default Main
Related
I am following a tutorial from https://auth0.com/blog/role-based-access-control-rbac-and-react-apps/ and it seems that author doesn't support it anymore.
The idea is simple: Once a user presses Login button (on Header.js) he is redirected to auth0 page. There he enters his data and is redirected back to localhost:3000/callback route. This is when handleAuthentication is triggered.
Unfortunately, I am facing an issue when setting the state when the setSession function is used.
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
in Auth (created by App)
in App
Here are the components:
App.js
import React from 'react'
import { Switch, Route, BrowserRouter as Router } from 'react-router-dom'
import Auth from './Auth';
import CallbackPage from "../pages/callback";
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<Auth>
<div style={{width: '1280px', margin: '0 auto'}}>
<Router>
<Switch>
<Route exact path='/' component={HomePage} />
<Route path='/callback' component={CallbackPage} />
</Switch>
</Router>
</div>
</Auth>
)
}
}
export default App;
Auth.js
import React, {Component} from 'react';
import auth0 from 'auth0-js';
import {AUTH_CONFIG} from '../auth0-variables';
import {AuthProvider} from '../authContext';
const auth = new auth0.WebAuth({
domain: AUTH_CONFIG.domain,
clientID: AUTH_CONFIG.clientId,
redirectUri: AUTH_CONFIG.callbackUrl,
audience: `https://${AUTH_CONFIG.domain}/userinfo`,
responseType: 'token id_token'
});
class Auth extends Component {
state = {
authenticated: false,
}
initiateLogin = () => {
auth.authorize();
};
handleAuthentication = () => {
auth.parseHash((error, authResult) => {
if (error) {
console.log(error);
console.log(`Error ${error.error} occured`);
return
}
this.setSession(authResult);
})
};
setSession(authResult) {
this.setState({
// This does not update the state!!
authenticated: true,
});
};
render() {
const authProviderValue = {
...this.state,
initiateLogin: this.initiateLogin,
handleAuthentication: this.handleAuthentication,
};
return (
<AuthProvider value={authProviderValue}>
{this.props.children}
</AuthProvider>
)
}
};
export default Auth;
Header.js (Can component can be found at https://github.com/itaditya/react-rbac-auth0-article-code/blob/master/src/components/Can.js)
import React, { useEffect, useReducer } from 'react';
import {
BrowserRouter as Router,
Link }
from 'react-router-dom';
import Login from '../Login';
import Logout from '../Logout';
import Can from '../Can';
import { AuthConsumer } from '../../authContext';
const Header = (props) => {
return (
<AuthConsumer>
{({ user }) => (
<Can role={user.role} perform='home-page:seeLogin'
yes={() => (
<Login />
)}
no={() => (
<Logout />
)}
/>
</AuthConsumer>
)
}
export default Header
And pages:
homePage:
import React from 'react';
const HomePage = () => {
return (
<div>
<Header />
</div>
)
};
export default HomePage;
Callback page
import React from 'react';
import { Redirect } from 'react-router-dom';
import { AuthConsumer } from '../authContext';
const Callback = props => (
<AuthConsumer>
{({ handleAuthentication }) => {
if (/access_token|id_token|error/.test(props.location.hash)) {
handleAuthentication();
}
return <Redirect to='/' />;
}}
</AuthConsumer>
);
export default Callback;
Any help would be appreciated.
I have a create profile page and an auth page (where one enters a code sent by text). I'd like to navigate to the auth page, when a profile is created.
I have a component for the create profile page and one for the auth page.
Based on the example here.
Relevant code:
// CreateProfileComponent.js
import React from 'react';
..
import { withRouter } from "react-router";
class CreateProfile extends React.Component {
constructor(props) {
super(props);
}
handleCreateProfile(e) {
e.preventDefault();
if (condition) {
createProfile(...);
this.props.history.push('/auth');
} else {
// re-render
}
}
render() {
return (
// data-testid="create-profile" - workaround (https://kula.blog/posts/test_on_submit_in_react_testing_library/) for
// https://github.com/jsdom/jsdom/issues/1937
<form onSubmit={this.handleCreateProfile.bind(this)} data-testid="create-profile">
...
</form>
);
}
}
export default withRouter(CreateProfile);
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import CreateProfile from './CreateProfileComponent';
import { TokenEntry } from './TokenEntryComponent';
ReactDOM.render(
<Router>
<ProtectedRoute exact path="/" component={ActivityList} />
<Route path="/login" component={CreateProfile.WrappedComponent} />
<Route path="/auth" component={TokenEntry} />
</Router>,
document.getElementById('root')
);
//createprofilecomponent.test.js
import React from 'react';
import {
LocationProvider,
createMemorySource,
createHistory
} from '#reach/router';
import { render, screen, fireEvent } from '#testing-library/react';
import CreateProfile from '../CreateProfileComponent';
const source = createMemorySource('/login');
const history = createHistory(source);
let displayName = null;
let phoneNumber = null;
let legalAgreement = null;
global.window = { location: { pathname: null } };
function Wrapper({children}) {
return <LocationProvider history={history}>{children}</LocationProvider>;
}
beforeEach(() => {
render(
<CreateProfile.WrappedComponent location={'/'} />,
{ wrapper: Wrapper }
);
});
it("navigates to /auth when good data entered", () => {
// setup
fireEvent.submit(screen.getByTestId('create-profile'));
expect(global.window.location.pathname).toEqual('/auth');
});
I'm getting
TypeError: Cannot read property 'push' of undefined
during tests and in Chrome.
What am I missing?
Use the react-router-dom
import { withRouter } from "react-router-dom";
Changed
export default withRouter(CreateProfile);
To
export const CreateProfileWithRouter = withRouter(CreateProfile);
Changed
<Route path="/login" component={CreateProfileWithRouter.WrappedComponent} />
To
<Route path="/login" component={CreateProfileWithRouter} />
Pull request created to include an example in withRouter's documentation.
Example gist
I'm trying to update my child component's photos from the parent components state. For all the other routes, the appropriate function was already invoked once the app was mounted. The component that renders cats, dogs, or computers is PhotoList.js
But now, I want to be able to enter a parameter after search (ex. /search/:id) and run a function called getImages in my Container.js to search for any type of picture from the Flickr API.
I tried using componentDidMount and invoking the getImages function with the match parameter inside of it but it doesn't seem to change the data props that's put into it. Does anyone have any suggestions as to how I can make this?
Here is Container.js
import React, {Component} from 'react';
import Photo from './Photo';
class Container extends Component {
componentDidMount() {
this.props.getImages(this.props.match.id)
}
render() {
return (
<div className="photo-container">
<h2>Results</h2>
<ul>
{this.props.data.map((photo,index)=>
<Photo
farm={photo.farm}
server={photo.server}
id={photo.id}
secret={photo.secret}
key={index}
/>
)}
</ul>
</div>
);
}
}
export default Container
Here is PhotoList.js
import React, {Component} from 'react';
import Photo from './Photo';
import NoResults from './NoResults';
class PhotoList extends Component {
render() {
return (
<div className="photo-container">
<h2>Results</h2>
<ul>
{this.props.data.map((photo,index)=>
<Photo
farm={photo.farm}
server={photo.server}
id={photo.id}
secret={photo.secret}
key={index}
/>
)}
</ul>
</div>
);
}
}
export default PhotoList;
Here is App.js
import React, {Component} from 'react';
import {
BrowserRouter,
Route,
Switch,
Redirect
} from 'react-router-dom';
import Search from './Search';
import Nav from './Nav';
import '../index.css';
import axios from 'axios';
import apiKey from './Config';
import NotFound from './NotFound';
import PhotoList from './PhotoList';
import NoResults from './NoResults';
import Container from './Container';
class App extends Component {
state= {
cats: [],
dogs: [],
computers: [],
searchResult: [],
loading: true
}
componentDidMount() {
this.getCats()
this.getDogs()
this.getComputers()
}
getCats=(query='cats')=> {
axios.get(`https://www.flickr.com/services/rest/?method=flickr.photos.search&api_key=${apiKey}&tags=${query}&per_page=24&page=1&format=json&nojsoncallback=1`)
.then(res=> {
const cats=res.data.photos.photo
this.setState({cats})
}).catch((error)=> {
console.log("There was an error parsing your data", error);
})
}
getDogs=(query='dogs')=> {
axios.get(`https://www.flickr.com/services/rest/?method=flickr.photos.search&api_key=${apiKey}&tags=${query}&per_page=24&page=1&format=json&nojsoncallback=1`)
.then(res=> {
const dogs=res.data.photos.photo
this.setState({dogs})
}).catch((error)=> {
console.log("There was an error parsing your data", error);
})
}
getComputers=(query='computers')=> {
axios.get(`https://www.flickr.com/services/rest/?method=flickr.photos.search&api_key=${apiKey}&tags=${query}&per_page=24&page=1&format=json&nojsoncallback=1`)
.then(res=> {
const computers=res.data.photos.photo
this.setState({computers});
}).catch((error)=> {
console.log("There was an error parsing your data", error);
})
}
getImages=(query)=> {
axios.get(`https://www.flickr.com/services/rest/?method=flickr.photos.search&api_key=${apiKey}&tags=${query}&per_page=24&page=1&format=json&nojsoncallback=1`)
.then (res=> {
const searchResult=res.data.photos.photo
this.setState({searchResult});
}).catch((error)=> {
console.log("There was an error parsing your data", error);
})
}
render() {
return (
<div className="container">
<Search getImages={this.getImages}/>
<Nav />
<Switch>
<Route exact path="/" render={()=> <Redirect to={'/cats'} />} />
<Route path='/cats' render={()=> <PhotoList data={this.state.cats}/>} />
<Route path='/dogs' render={()=> <PhotoList data={this.state.dogs} />} />
<Route exact path='/computers' render={()=> <PhotoList data={this.state.computers} />} />
<Route path='/search/:id' render={(props)=> <Container {...props} getImages={this.getImages} data={this.state.searchResult} />} />
<Route component={NotFound}/>
</Switch>
</div>
)
}
}
export default App;
Assuming your are using react-router-dom 4 and above.
Try
import React, { Component } from "react";
import { withRouter } from "react-router-dom"; //<-- import this
import Photo from "./Photo";
class Container extends Component {
componentDidMount() {
// Your this.props.match.id is likely undefined
this.props.getImages(this.props.match.params.id); // <-- Change here
}
...
}
export default withRouter(Container); // <-- Change this
My app.js:
import React from 'react';
import { Router, Route, Link, IndexRoute, hashHistory, browserHistory } from 'react-router';
import Home from './components/home.jsx';
const loadAsync = (promise) => (location, callback) => {
promise
.then(module => callback(null, module.default))
.catch(e => console.warn('Could not load route component', e));
}
class App extends React.Component {
render() {
return(
<Router history={hashHistory}>
<Route path="/" component={Home} />
<Route path="/hello/:foo" getComponent={loadAsync(System.import('./components/hello.jsx'))} />
</Router>
)
}
}
export default App;
My hello.jsx:
import React from 'react';
class Hello extends React.Component {
render() {
return (<h1>Hello {this.props.matches.foo}!</h1>);
}
}
export default Hello;
The home.jsx contains a Link to "/hello/World".
How can read the "foo" variable in hello.jsx?
Do I have to pass it in loadAsync() and how ?
I'm trying to get a very basic app going using Meteor 1.3, React and React-Router. The pages are rendering but am having issues with getting data passed through. I've done some research but unable to find much with this particular mix including use of the container pattern.
All that the app needs to do is surface all items in the 'Thingies' collection on the Test page. Unsure if it's the data publication, routing, container or something else that's incorrect?
The debugging console lines all show 0 in the collection even though the mongo shell definitely shows items in there.
Structure of my project: http://i.stack.imgur.com/WZXFa.png
things.js
import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import { check } from 'meteor/check';
export const Thingies = new Mongo.Collection('thingies');
if (Meteor.isServer) {
// This code only runs on the server
Meteor.publish('thingies', function thingiesPublication() {
return Thingies.find();
});
}
Test.jsx
import { Meteor } from 'meteor/meteor';
import { Thingies } from '../../api/things.js';
import { createContainer } from 'meteor/react-meteor-data';
import React, { Component } from 'react';
class TestContainer extends Component {
render(){
console.log('Props: ' + this.props.thingies.length);
let RenderThings = this.props.thingies.map(thing => {
return <li key={thing._id}>{thing.text}</li>
});
return (
<div>
<h1>Test this</h1>
<ul>
{ RenderThings }
</ul>
</div>
);
}
}
TestContainer.propType = {
thingies: React.PropTypes.array
};
export default createContainer(() => {
Meteor.subscribe('thingies');
console.log('Container: ' + Thingies.find({}).count());
return {
thingies: Thingies.find({}).fetch(),
};
}, TestContainer);
routes.jsx
import React from 'react';
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
// route components
import App from '../../ui/layouts/App.jsx';
import TestContainer from '../../ui/pages/Test.jsx';
import Index from '../../ui/pages/Index.jsx';
export const renderRoutes = () => (
<Router history={browserHistory}>
<Route path="/" component={ App }>
<IndexRoute component={ Index } />
<Route path="index" component={Index}/>
<Route path="test" component={TestContainer}/>
</Route>
</Router>
);
Navigation.jsx
import React from 'react';
import { IndexLink, Link } from 'react-router';
export const Navigation = () => (
<div>
<h4>Navigation</h4>
<ul>
<li><IndexLink to="/" activeClassName="active">Home</IndexLink></li>
<li><Link to="index" activeClassName="active">Index</Link></li>
<li><Link to="test" activeClassName="active">Test</Link></li>
</ul>
</div>
)
App.jsx
import React, { Component } from 'react';
import { Navigation } from '../components/Navigation.jsx';
const App = ( { children } ) => (
<div>
<Navigation />
{ children }
</div>
)
export default App;
Many thanks in advance. I'm sure I'm missing something obvious!
I would try to change the Test.jsx Container code a little bit:
export default createContainer(() => {
if (Meteor.subscribe('thingies').ready()) {
console.log('Container: ' + Thingies.find({}).count());
return {
thingies: Thingies.find({}).fetch()
};
} else {
return {
thingies: null
};
}
}, TestContainer);
EDIT: Try replace your publish method with:
if (Meteor.isServer) {
// This code only runs on the server
Meteor.publish('thingies', function() {
return Thingies.find({});
});
}