I'm coding login function that would return to home component after finish save token data to client.
However, when it go back to homepage, i can not use setState because do not have any componentWillMount or componentDidMount function was called.
Login.js
axios.post(`${Config.API_URL}/users/login`, param)
.then(response => {
if (response) {
this.setState({
errorCode : response.status
});
}
if(response.status===ErrorCode.SUCCESS){
var authorization = {"authorization": response.headers.authorization.toString()}
SessionStorage.saveLocalStorage(authorization);
this.props.history.push("/");
}
})
App.js
componentWillMount() {
if(SessionStorage.isAuthorization()){
this.setState({
isAuthorization : true
});
}
console.log('Component Will MOUNT!')
}
ComponentWillMount() never been called so can not set value for isAuthorization = true anyway.
A couple of notes:
1) I'm assuming App.js is actually the holder of all your routes. In this case, App.js is never unmounted from ReactDOM. What you want to do is define a separate component for your "/" route, keeping your App.js clean, focusing only on rendering routes. We will call this component Home.
2) I'm not familiar with any library that uses SessionStorage(). You might be looking for the sessionStorage native JS library. This will have methods called setItem and getItem for storing and retrieving your token.
3) With the separate Home component, we can call componentDidMount() to retrieve the token.
With that in mind we can do the following as a template on how you can achieve the functionality you're looking for: https://codesandbox.io/s/suspicious-leftpad-moqs2
App.js
class App extends React.Component {
render() {
return (
<Provider store={store}>
<BrowserRouter>
<div>
<Switch>
<Route path="/" component={Home} exact />
<Route path="/login" component={Login} />
</Switch>
</div>
</BrowserRouter>
</Provider>
);
}
}
Home.js
import React from "react";
import { Link } from "react-router-dom";
class Home extends React.Component {
state = {
token: ""
};
componentDidMount() {
const token = sessionStorage.getItem("authorization");
if (token) {
this.setState({
token: token
});
}
}
render() {
return (
<div>
<Link to="/login">Login</Link>
<p>{this.state.token}</p>
</div>
);
}
}
export default Home;
Login.js
import React from "react";
class Login extends React.Component {
handleClick = () => {
var token = 210;
sessionStorage.setItem("authorization", token);
this.props.history.push("/");
};
render() {
return (
<div>
<button onClick={this.handleClick}>Login</button>
</div>
);
}
}
export default Login;
Related
I am facing issue in passing state from App Component through React Router. In the App component's ComponentwillMount function, the state is loaded through an API, which is passed to Login Component by specifying it in the render function of the Route Component.
But, the Login Component is loaded prior to App setState. I need to pass this state to all other Components. Please help !
import React, { Component } from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = {
language: 'en',
labels: null,
};
}
componentDidMount() {
let language = getLanguage(); //from url
this.setState({ language }, async () => {
await this.getLabels();
});
}
getLabels = () => {
//Hit Api to fetch labels on basis of language set
this.setState({ labels: data });
};
render() {
return (
<div className='App'>
<Router>
<Switch>
<Route
exact
path='/'
render={(props) => (
<Login labels={this.state.labels} {...props} />
)}
/>
</Switch>
</Router>
</div>
);
}
}
export default App;
import React, { Component } from 'react';
export default class Login extends Component {
render() {
console.log(this.props.labels);
}
}
this.props.labels is undefined in Login Component.
Can you try showing a loder untill your api call was successfull.
import React, { Component } from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = {
language: 'en',
labels: null,
fetchingLabels:true
};
}
componentDidMount() {
let language = getLanguage(); //from url
this.setState({ language }, async () => {
await this.getLabels();
});
}
getLabels = () => {
//Hit Api to fetch labels on basis of language set
this.setState({ labels: data, fetchingLabels:false });
};
render() {
if(this.state.fetchingLabels){
return 'I am loading' // you can add your loader here
}
return (
<div className='App'>
<Router>
<Switch>
<Route
exact
path='/'
render={(props) => (
<Login labels={this.state.labels} {...props} />
)}
/>
</Switch>
</Router>
</div>
);
}
}
export default App;
import React, { Component } from 'react';
export default class Login extends Component {
render() {
console.log(this.props.labels);
}
}
Im having trouble accessing data from my backend using express, I am also sort of confused on how i should set up my routing. Do I only need express routes for when I have to dig something out of my database?
My User component
import React from 'react';
class User extends React.Component {
state = {
username: ""
}
componentDidMount() {
fetch("/api/:user")
.then(res =>res.json()
.then(data => {
console.log("data", JSON.stringify(data, null, 4));
this.setState({data})
}))
}
render() {
return (
<div>
{this.state.username}
</div>
)
}
}
export default User;
my user routes, the route is /api/:user
router.get("/:user", (req, res)=>{
// find user
console.log(req.params);
User.find({username:req.params.user}, (err, foundUser)=>{
if(err) {
console.log(err);
} else {
res.json(foundUser);
}
});
});
when i console.log(req.params) it returns :user, not the actual user requested
I am giving a sample code for you to learn.
In the App.js we define the routes using react-router-dom package.
import React from "react";
import ReactDOM from "react-dom";
import {
BrowserRouter as Router,
Route,
Redirect,
Switch,
Link
} from "react-router-dom";
import Users from "./Users";
import User from "./User";
function App() {
return (
<Router>
<Link to="/">Users</Link>
<Switch>
<Route path="/" exact component={Users} />
<Route path="/:userId/" exact component={User} />
<Redirect to="/" />
</Switch>
</Router>
);
}
export default App;
In Users component, we get a list of users from jsonplaceholder api and list them, give a dynamic link with a userId to the User component.
import React from "react";
import { Link } from "react-router-dom";
class Users extends React.Component {
state = {
users: []
};
componentDidMount() {
fetch("https://jsonplaceholder.typicode.com/users")
.then(res => res.json())
.then(users => this.setState({ users }));
}
render() {
return (
<ul>
{this.state.users.map(user => (
<li key={user.id}>
<Link to={`/${user.id}`}> {user.name}</Link>
</li>
))}
</ul>
);
}
}
export default Users;
In the user component, we get the userId param from this.props.match.params.userId,
and using that userId we call another api to get user details.
import React from "react";
class User extends React.Component {
state = {
user: null
};
componentDidMount() {
fetch(`https://jsonplaceholder.typicode.com/users/${this.props.match.params.userId}`)
.then(res => res.json())
.then(user => {
this.setState({ user });
});
}
render() {
const { user } = this.state;
if (!user) return <div>Loading user...</div>;
return (
<div>
<h1>User Name: {user.name}</h1>
<p>Email: {user.email}</p>
<p>Website: {user.website}</p>
</div>
);
}
}
export default User;
Note that we dynamically constructed the user detail api url by with template literal using the backtick character. This was one of the problems you had in your code.
Codesandbox
In your url, the :user is a way to say in the backend code "a value goes at this place", and this must be the user id from the database I presume.
So in the React code you should do :
const userId = 4 // the id of the user that you want to fetch
fetch(`/api/${userId}`)
I'm pretty new to React, and I'm having an issue with State in my App.jsx. My react-router code is in App.js, which after logging in the default page (HomePage) will load. I want to read the state after login to render my Header page if there is a user loaded in state. The issue I have is that the state in App.jsx is still null.
After login, my state shows that user is populated, but App.jsx is always null. I've tried pulling "user" from both props, and state, and I've tried adding user to mapState.
Here is what I have in my App.jsx:
import React from "react";
import { Router, Route } from "react-router-dom";
import { connect } from "react-redux";
import { history } from "../_helpers";
import { alertActions } from "../_actions";
import { PrivateRoute } from "../_components";
import { HomePage } from "../HomePage";
import { LoginPage } from "../LoginPage";
import { RegisterPage } from "../RegisterPage";
import Header from "../Header/Header";
class App extends React.Component {
constructor(props) {
super(props);
history.listen((location, action) => {
// clear alert on location change
this.props.clearAlerts();
});
}
render() {
const { alert } = this.props;
var user = this.state;
let header = null;
if (user) {
header = <Header />;
}
return (
<div>
{header}
<div className="jumbotron">
<div className="container">
<div className="col-sm-8 col-sm-offset-2">
{alert.message && (
<div className={`alert ${alert.type}`}>{alert.message}</div>
)}
<Router history={history}>
<div>
<PrivateRoute exact path="/" component={HomePage} />
<Route path="/login" component={LoginPage} />
<Route path="/register" component={RegisterPage} />
</div>
</Router>
</div>
</div>
</div>
</div>
);
}
}
function mapState(state) {
const { alert } = state;
return { alert };
}
const actionCreators = {
clearAlerts: alertActions.clear
};
const connectedApp = connect(
mapState,
actionCreators
)(App);
export { connectedApp as App };
Any help, or suggestions of a better way to do this, would be greatly appreciated.
You have to initialize the state fist.
You have 2 options to do that:
Within your constructor: call this.state = {...}
Outside: somewhere in your component call state = {...}
Both of these calls will initialize your state to the default state and after that, calls to this.state will be defined.
Hope this helps.
The state isn't initialized. Be careful with that. var user = this.state can be removed and then you can initialize the state. Also, add user to the state object and instead of if (user) use if (this.state.user). Then you can call state. I think this might be what you need.
I want to redirect to the home page when some condition returns null or false but the action of Redirect is not triggered.
import { Link, Redirect } from "react-router-dom";
if(localStorage.getItem("example") === null || localStorage.getItem("example") === false){
return <Redirect to="/" />
}
I put this code inside in a simple function triggered in one OnClick and componentDidMount(), but it's not working.
You could use Redirect to home page, based on redirect flag that could be changed by using setState in onClickHandler or handleSubmit.
import { Redirect } from "react-router-dom";
class MyComponent extends React.Component {
state = {
redirect: false
}
handleSubmit () {
if(localStorage.getItem("example") === null || localStorage.getItem("example") === false){
return this.setState({ redirect: true });
}
}
render () {
const { redirect } = this.state;
if (redirect) {
return <Redirect to='/'/>;
}
return <YourForm/>;
}
You need to use the Redirect inside render. It is a React Component which renders and then sends the user to the desired path:
import React, { Component } from "react";
import { Route, Switch, Redirect } from "react-router-dom";
class RootPage extends React.Component {
state = {
isLoggedOut: false
};
onClick = () => {
this.setState({
isLoggedOut: true
});
};
render() {
return (
<div>
{this.state.isLoggedOut && <Redirect to="/logout" />}
<button onClick={this.onClick}>Logout</button>
</div>
);
}
}
const Log = () => <h1>Logout</h1>;
class App extends Component {
render() {
return (
<div>
<nav className="navbar navbar" />
<Switch>
<Route exact path="/" component={RootPage} />
<Route exact path="/logout" component={Log} />
</Switch>
</div>
);
}
}
export default App;
When you click on the logout button it will redirect you to the rootPath.
Here is the Demo: https://codesandbox.io/s/q9v2nrjnx4
Have a look at this example in the official docs.
<Redirect /> should be inside your render method if you use a class component. or if you use a function component it should be in what's returned by it.
Example bellow:
import { Component } from 'react';
const PrivateComponent = (props) => {
return(
localStorage.getItem("example")
? <RandomComponent />
: <Redirect to="/signin" />
)
}
I'm using React with the Wordpress REST API. The issue I am having is I cannot seem to wrap my head around how to (properly) use the component lifecycles to update the Post component when the slug property changes on the root App Component and fetching async data.
The way I have it set up currently, the App component state looks something like this:
this.state = {
pages: this.getPages(),
slug: this.getSlug(),
homePage: this.fetchPost('home'),
};
So the pages property is a promise and the App component initially renders a Spinner component. Eventually the async call receives a response. I perform a filter on the array of post objects to look for the current page post.
const thePage = this.state.pages.filter(page => {
return page.slug === slug;
});
The filter returns an array with one object (this current page). I update the state with this.setState({post: thePage[0]});
When I change change routes with react-router-dom, the slug and post are not updating. Below is my code.
index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import App from './components/App/App';
// Take the React component and show it on the screen
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root'));
App JS:
// App.js
import React, {Component} from 'react';
import { Route, withRouter } from 'react-router-dom';
import axios from 'axios';
import './App.scss';
import {wpAPI} from '../..//api';
import {/*loadState,*/ saveState} from '../loadState/loadState';
import FrontPage from '../../pages/FrontPage';
import Header from '../Header/Header';
import Post from '../Post/Post';
import Sidebar from '../Sidebar/Sidebar';
import Footer from '../Footer/Footer';
import Spinner from '../Spinner/Spinner';
// Create a React Component
class App extends Component {
constructor(props) {
super(props);
// Bindings
this.getPages = this.getPages.bind(this);
this.getPages();
this.state = {
isHome: false,
slug: this.props.location.pathname,
fetchingPages: true,
fetchingPost: true,
};
console.log('App State: (constructor)');
console.log(this.state);
}
/**
* Fetch Data
* #return {Promise}
*/
getPages = async () => {
const response = await axios.get(wpAPI['pages']);
this.setState({
fetchingPages: false,
pages: response.data
});
saveState(this.state);
}
getPage = (slug) => {
const thePage = this.state.pages.filter(page => {
return page.slug === slug.replace('/', '');
});
this.setState({
isHome: false,
fetchingPost: false,
post: thePage[0],
slug: slug,
});
}
/**
* The component has mounted. Fetch post based on slug
*/
componentDidMount() {
console.log('App State: (componentDidMount)');
console.log(this.state);
console.log('App Props: (componentDidMount)');
console.log(this.props);
}
componentDidUpdate(prevProps, prevState) {
console.log('App State: (componentDidUpdate)');
console.log(this.state);
const {fetchingPages, fetchingPost, isHome} = this.state;
const slug = this.props.location.pathname;
if (this.state.slug !== this.props.location.pathname) {
console.log('Slugs Dont match, getting page');
this.getPage(slug);
}
if (slug === '/' && !isHome) {
console.log('Setting isHome True');
this.setState({
isHome: true,
fetchingPost: false
});
}
if (fetchingPages === false && fetchingPost === true) {
console.log('Fetching Post');
this.getPage(slug);
}
}
renderContent() {
const {post, fetchingPost} = this.state;
if (!fetchingPost) {
return (
<section id="app" className="animated fadeIn">
<Header />
<main>
<Route path="/" exact render={(props) => <FrontPage {...props} /> } />
<Route path="/:slug" exact render={(props) => <Post post={post} /> } />
</main>
<Sidebar />
<Footer />
</section>
)
}
return <Spinner message='loading data...' />;
}
render() {
return this.renderContent();
}
};
export default withRouter(App);
Post.js
import React, {Component} from 'react';
import {withRouter} from 'react-router-dom';
import './Post.scss';
import Spinner from '../Spinner/Spinner';
class Post extends Component {
constructor(props) {
super(props);
this.state = {
slug: props.slug,
post: props.post,
};
}
componentDidMount() {
console.log('Post State: (componentDidUpdate)');
console.log(this.state);
}
componentDidUpdate() {
console.log('Post State: (componentDidUpdate)');
}
render() {
if ( this.state.post ) {
const {post} = this.state;
return (
<div className={`post post-${post.id} ${post.slug} animated fadeIn`}>
<header>
<h1 className="small-caps" dangerouslySetInnerHTML={{__html: post.title.rendered}}></h1>
</header>
<section id="content" dangerouslySetInnerHTML={{__html: post.content.rendered}}></section>
</div>
)
}
return <Spinner message='Fetching Post...'/>
}
}
export default withRouter(Post);
There's nothing in your code to say that when your App receives new props from the withRouter HOC, then update state.slug.
You could add:
this.setState({
slug: this.getSlug();
});
to your componentDidUpdate() function, however I'm not sure why you need it as state when it's available as a prop anyway in this.props.location.pathname which is being passed down to your FrontPage component, and could just as easily be passed down to your Posts component in the same way.