React Component does not update until refresh - node.js

I have the following code:
In App.js:
class App extends React.Component {
constructor() {
super();
this.state = {
loggedStatus: {
username: undefined,
isLoggedIn: undefined,
}
}
}
componentDidMount() {
let username = undefined;
let isLoggedIn = undefined;
if (localStorage.getItem("token")) {
fetch("https://localhost:8000/user", {
method: "POST",
headers: {
"Authorization": `Bearer ${localStorage.getItem("token")}`
}
}).then(response => response.json()).then(response => {
if (response.success) {
username = response.username;
isLoggedIn = true;
} else {
username = undefined;
isLoggedIn = false;
localStorage.removeItem("token");
}
this.setState({
loggedStatus: {
username: username,
isLoggedIn: isLoggedIn
}
})
})
}
}
render() {
return (
<div className="App">
<Router>
<Navbar loggedStatus={this.state.loggedStatus}/>
<Switch>
<Route path="/register">
<RegisterForm />
</Route>
<Route path="/login">
<LoginForm />
</Route>
</Switch>
</Router>
</div>
)
}
}
and in Navbar.js:
logout = () => {
localStorage.removeItem("token");
}
render() {
return (
<nav className="Navbar">
{this.props.loggedStatus.isLoggedIn ?
<>
<ul className="Navbar-list">
<li className="Navbar-item">
<span className="Navbar-greeting">Hello, {this.props.loggedStatus.username}</span>
</li>
<li className="Navbar-item">
<button className="Navbar-logoutBtn" onClick={this.logout}>Sign Out</button>
</li>
</ul>
</>
:
<>
<ul className="Navbar-list">
<li className="Navbar-item">
<Link to="/register" className="Navbar-link">Sign Up</Link>
</li>
<li className="Navbar-item">
<Link to="/login" className="Navbar-link">Log In</Link>
</li>
</ul>
</>
}
</nav>
)
}
The problem I'm having is I would like my navbar component to update when either the user logs in, or logs out. With my current code, I have to refresh the page in order for it to update. I've been messing around with things with no luck. I understand that componentDidMount is only called once through the entire process, which is why setState is only called upon refresh.
Edit: Login.
fetch(`${this.apiURL}/user/login`, {
method: "POST",
body: JSON.stringify(user),
headers: {
"Accept": "application/json",
"Content-Type": "application/json"
}
})
.then(response => response.json())
.then(response => {
if (response.success) {
status = {
statusMsg: <p className="LoginForm-statusMsg">{response.statusMsg}</p>
}
localStorage.setItem("token", response.token);
this.setState({
status: status
}, () => setTimeout(() => {
this.props.history.push("/");
}, 5000));
} else {
status = {
statusMsg: <p className="LoginForm-statusMsg">{response.statusMsg}</p>
}
this.setState({
status : status
})
}
});
}
}

setState({}) always forces to re-render. (unless you return false in: shouldComponentUpdate(nextProps, nextState)) You can check this by putting a console log in
componentDidUpdate(prevProps, prevState) {
console.log("Component did update")
}
It's not clear what your JobsScreenTabs component consists of but make sure that for changes you expect to happen inside the JobsScreenTabs component it actually changes its state. Pass properties from your WorkshopJobsScreen component or make changes directly in the JobsScreenTabs component.
Also important:
Using State Correctly
There are three things you should know about setState().
Do Not Modify State Directly
For example, this will not re-render a component:
// Wrong
this.state.comment = 'Hello';
Instead, use setState():
// Correct
this.setState({comment: 'Hello'});
React may batch multiple setState() calls into a single update for performance.
Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.
Neither parent nor child components can know if a certain component is stateful or stateless, and they shouldn’t care whether it is defined as a function or a class.
This is why state is often called local or encapsulated. It is not accessible to any component other than the one that owns and sets it.
So if you wish to make changes in your component make sure to manipulate the state of the correct component.
Read more about React lifecycles at: https://reactjs.org/docs/state-and-lifecycle.html
So just some general info. Answer starts here:
You can pass your state in props from app js to navbar component through the route:
let loggedStatus = {
username: undefined,
isLoggedIn: false
}
<Route path="/" render={(props) => <NavBar props={loggedStatus} {...props} /> } exact />
In NavBar you can access it:
export class NavBar extends Component {
constructor(props) {
super(props);
console.log(props)
this.state = {
isLoggedIn: this.props.loggedStatus.isLoggedIn

As I'm not aware of the login flow you are following. But as far as what I understood. The code below should match your requirement.
In NavBar.js
logout = () => {
this.props.handleLogout()
}
In app.js
handleLogout = () => {
localStorage.removeItem("token");
this.setState({
loggedStatus: {
username: undefined,
isLoggedIn: false
}
})
}
render(){
return(
..
..
<Navbar loggedStatus={this.state.loggedStatus} handleLogout={this.handleLogout}/>
)
}
The state changes so it will re-render the component.
Hope this helps you.

Related

How can I take a value from an input tag in a TSX component, and use that value in a Node JS file in a different directory?

I'm working on a personal project where I'm pulling an API through Fetch; at the moment I can send the call from my index.js file to a TSX component that calls the API URL when my SearchButton component is clicked, but the search term needs to be declared in index.js.
Here's my SearchButton code (TSX):
import React, { useState } from 'react'
function SearchButton() {
const [ newsResponse, setNewsResponse ]= useState(null);
function queryOnClick() {
fetch(`http://localhost:4000/news-api`, {
headers: { 'Content-Type': 'application/json' }
})
.then((response) => response.json())
.then((result) => {
console.log('result:', result);
setNewsResponse(result);
})
.catch((ex) => {
console.log('error:', ex);
});
}
return (
<div className="theme--white">
<button className="search__button padding-1 margin-1 margin-left-6" onClick={queryOnClick}>
Click to search
</button>
{newsResponse && newsResponse.articles ? (
<div className="results__container padding-2 theme--mist">
{newsResponse.articles.map((article: {
title: React.ReactNode;
author: string;
content: string;
url: string;
}) => (
<div className="article__container box-shadow padding-2 margin-4 margin-left-6 margin-right-6 theme--white">
<h2 className="article__title padding-bottom-2 margin-bottom-2">{article.title}</h2>
<h3 className="article__author padding-bottom-2 margin-bottom-2">Written by: {article.author || 'An uncredited author'}</h3>
<p className="article__content">
{article.content.length > 150 ?
`${article.content.substring(0, 150)}... [Article shortened - Click the URL below to read more]` : article.content
}
</p>
<div className="article__url margin-top-2">
<p>
<p>Source:</p>
<a href={article.url}>{article.url}</a>
</p>
</div>
</div>
))}
</div>
) : null}
</div>
);
}
export default SearchButton;
I want to change that so a user can search for an article from the API by using a HTML input to submit a topic which would amend the API URL. For instance, if I searched Bitcoin, it would search https://API-${Bitcoin}.com. Due to CORS policy blocking, I can't just call the API in my TSX file as it has to go from localhost:3000 > localhost:4000 via the Node JS file.
At the moment, my input renders the user's query into the console, but I can't seem to get it over to my index.js file. How can I pass a value that's either in the console.log, or from the input's value, through to my Node JS index.js file?
Here's my SearchBar file that handles my Input (TSX):
import React, { Component } from 'react';
type SearchBarProps = {
searchNews: (text: string) => void;
}
type SearchBarState = {
searchString: string;
}
class SearchBar extends Component<SearchBarProps, SearchBarState> {
static defaultProps = {
searchNews: (text: string) => {}
}
state = {
searchString: ''
}
searchNews = (e: any) => {
const { searchString } = this.state
if(e.key === 'Enter' && searchString !== '') {
e.preventDefault();
e.stopPropagation();
this.props.searchNews(searchString)
console.log(searchString)
}
}
onSearchTextChange = (e: any) => {
this.setState({
searchString: e.target.value.trim()
})
}
render() {
return (
<div>
<form>
<div>
<input
id="search"
type="search"
value={this.state.searchString}
onChange={this.onSearchTextChange}
onKeyPress={e => this.searchNews(e)} placeholder="Search" />
</div>
</form>
</div>
);
}
}
export default SearchBar;
...And here's my index.js Node JS file (JS):
/*
* Libs
*/
const express = require('express');
const fetch = require('node-fetch');
const cors = require('cors');
const app = express();
/*
* Constants
*/
const PORT = 4000;
const API_KEY = 'x';
const SEARCH_QUERY = "Bitcoin";
const SORT_BY = "popularity";
const PAGE_SIZE = 10;
/*
* Setup CORS - This is needed to bypass NewsAPI CORS Policy Blocking by rerouting request to localhost
*/
const corsOptions = {
origin: 'http://localhost:3000',
optionsSuccessStatus: 200
};
app.use(cors(corsOptions));
/*
* Setup to request NewsAPI data using Fetch API
*/
app.get('/news-api', function (req, res) {
fetch(`https://newsapi.org/v2/everything?q=${SEARCH_QUERY}&sortBy=${SORT_BY}&pageSize=${PAGE_SIZE}&apiKey=${API_KEY}`, {
headers: { 'Content-Type': 'application/json' }
})
.then((response) => response.json())
.then((result) => {
console.log('result:', result);
res.json(result);
})
.catch((ex) => {
console.log('error:', ex);
res.status(400).send({
message: 'This is an error!',
error: ex
});
});
})
/*
* Start Backend API Proxy server
*/
app.listen(PORT, () => {
console.log(`=================`)
console.log(`API Connected!`)
console.log(`Listening at http://localhost:${PORT}`)
console.log(`=================`)
})
TLDR:
I have a TSX component that is an input (A - value={this.state.searchString}).
I want that input's value to go to a Node JS file to append a URL via a const (B - const SEARCH_QUERY).
I know what to pull from A, and where to put it in B, but don't know how to do so.
Full tech stack
Using Fetch API, React, TypeScript, Node JS and Webpack.
File paths
SearchButton: project/frontend/src/components/SearchButton/SearchButton.tsx
SearchBar: project/frontend/src/components/SearchBar/SearchBar.tsx
Node JS handler: project/backend/index.js
Essentially what you are asking here is how to pass data from the frontend to the backend. The way to do this is by including the user's search term in your fetch request to the backend. You can either include it in the body of a POST request or include it as a query string in the URL. You would need to use the body for passing large amounts of data, but something as simple as a search term can be done with a query string.
Front End
Include the current search term as a query parameter of your fetch request. I am using encodeURIComponent to apply percent-encoding to special characters.
function queryOnClick() {
// applies percent-encoding to special characters
const search = encodeURIComponent(searchString);
const url = `http://localhost:4000/news-api?search=${search}`;
fetch(url, {
...
You are missing the communication between your SearchButton and SearchBar components. I am not sure where these two components are in relation to each other on your page. If they are siblings then you will need to lift the searchString state and the queryOnClick function up to a shared parent.
I rearranged all of your components so that you have access to the right state in the right places.
import React, { useState } from "react";
function SearchButton({ onClick }: { onClick: () => void }) {
return (
<button
className="search__button padding-1 margin-1 margin-left-6"
onClick={onClick}
>
Click to search
</button>
);
}
interface SearchBarProps {
searchNews: () => void;
searchString: string;
setSearchString: (s: string) => void;
}
function SearchBar({ searchNews, searchString, setSearchString }: SearchBarProps) {
const handleKeyPress = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter" && searchString !== "") {
e.preventDefault();
e.stopPropagation();
searchNews();
}
};
const onSearchTextChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setSearchString(e.target.value.trim());
};
return (
<div>
<form>
<div>
<input
id="search"
type="search"
value={searchString}
onChange={onSearchTextChange}
onKeyPress={handleKeyPress}
placeholder="Search"
/>
</div>
</form>
</div>
);
}
interface Article {
title: string;
author: string;
content: string;
url: string;
}
interface NewsResponse {
articles: Article[];
}
function ArticleList({ articles }: NewsResponse) {
return (
<div className="results__container padding-2 theme--mist">
{articles.map((article) => (
<div className="article__container box-shadow padding-2 margin-4 margin-left-6 margin-right-6 theme--white">
<h2 className="article__title padding-bottom-2 margin-bottom-2">
{article.title}
</h2>
<h3 className="article__author padding-bottom-2 margin-bottom-2">
Written by: {article.author || "An uncredited author"}
</h3>
<p className="article__content">
{article.content.length > 150
? `${article.content.substring(
0,
150
)}... [Article shortened - Click the URL below to read more]`
: article.content}
</p>
<div className="article__url margin-top-2">
<p>
<p>Source:</p>
<a href={article.url}>{article.url}</a>
</p>
</div>
</div>
))}
</div>
);
}
function SearchPage() {
const [newsResponse, setNewsResponse] = useState<NewsResponse | null>(null);
const [searchString, setSearchString] = useState("");
function queryOnClick() {
// applies percent-encoding to special characters
const search = encodeURIComponent(searchString);
const url = `http://localhost:4000/news-api?search=${search}`;
fetch(url, {
headers: { "Content-Type": "application/json" }
})
.then((response) => response.json())
.then((result) => {
console.log("result:", result);
setNewsResponse(result);
})
.catch((ex) => {
console.log("error:", ex);
});
}
return (
<div className="theme--white">
<SearchBar
searchNews={queryOnClick}
searchString={searchString}
setSearchString={setSearchString}
/>
<SearchButton onClick={queryOnClick} />
{newsResponse && newsResponse.articles ? (
<ArticleList articles={newsResponse.articles} />
) : null}
</div>
);
}
export default SearchPage;
Back End
You need to access the search term from the search parameter of the request URL. We use the req.params property to get a dictionary of params. We can use your previous search term "Bitcoin" as the default value if there was no search param on the request.
I'm not certain if we need to encode again here or not -- you'll want to play with that.
app.get('/news-api', function (req, res) {
const searchQuery = req.params.search || "Bitcoin";
fetch(`https://newsapi.org/v2/everything?q=${searchQuery}&sortBy=${SORT_BY}&pageSize=${PAGE_SIZE}&apiKey=${API_KEY}`, {
...

API call in render method in React inside map

I have a userId array and I need to show the list of names related to that array. I want to call API call inside the render method and get the username. But this is not working. How can I fix this issue?
Below is my render method:
render(){
...
return(
<div>
{this.state.users.map(userId => {
return (
<div> {this.renderName(userId )} </div>
)
})}
</div>
)
...
}
Below is the renderName function:
renderName = (userId) => {
axios.get(backendURI.url + '/users/getUserName/' + userId)
.then(res => <div>{res.data.name}</div>)
}
Basically you cannot use asynchronous calls inside a render because they return a Promise which is not valid JSX. Rather use componentDidMount and setState to update the users array with their names.
Generally, you do not change state or fetch data in the render method directly. State is always changed by actions/events (clicks, input or whatever). The render method is called everytime a prop/state changes. If you change the state within the render method directly, you end up having an infinite loop.
You should use the lifecycle methods or hooks to load data from an api. Here's an example from the official React FAQ: https://reactjs.org/docs/faq-ajax.html
This will not render anything as the API calls are asynchronous and since renderName function isn't returning anything, it'll return undefined.
You should create a function, which will call api for all the userIds and update in state
getNames = () => {
const promises = [];
this.state.users.forEach((userId) => {
promises.push(axios.get(backendURI.url+'/users/getUserName/'+userId));
})
// Once all promises are resolved, update the state
Promise.all(promises).then((responses) => {
const names = responses.map((response) => response.data.names);
this.setState({names});
})
}
Now you can call this function in either componentDidMount or componentDidUpdate, whenever users data is available.
And finally, you can iterate over names directly and render them
<div>
{this.state.names.map((name) => {
return <div> {name} </div>;
})}
</div>
You could make user name it's own component:
const request = (id) =>
new Promise((resolve) =>
setTimeout(resolve(`id is:${id}`), 2000)
);
const UserName = React.memo(function User({ userId }) {
const [name, setName] = React.useState('');
React.useEffect(() => {
//make the request and set local state to the result
request(userId).then((result) => setName(result));
}, [userId]);
return <div> {name} </div>;
});
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
users: [1, 2],
};
}
render() {
return (
<ul>
{this.state.users.map((userId) => (
<UserName key={userId} userId={userId} />
))}
</ul>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
export default ()=> {
let [users,setUsers] = useState([]);
useEffect(()=>{
let fetchUsersInfoRemote = Promise.all([...Array(10)].map(async (_,index)=>{
try {
let response = await axios.get(`https://jsonplaceholder.typicode.com/posts/${index+1}`);
return response.data;
}
catch(error) {
return ;
}
}));
fetchUsersInfoRemote.then(data=> setUsers(data));
},[]);
return (
<div className="App">
<ul>
{
users.map(user=>(<li><pre>{JSON.stringify(user,null,2)}</pre></li>))
}
</ul>
</div>
);
}

React component not rendering after state change

As the title says, when my state changes in my component, the sub components aren't rerendering.
class App extends Component {
constructor() {
super()
this.state = {
url: ""
}
this.handleWorkerSelect = this.handleWorkerSelect.bind(this)
}
handleWorkerSelect(url) {
this.setState({ url })
}
render() {
return (
<div className="App">
<Workers className="workers" handleClick={this.handleWorkerSelect}/>
<HermesWorker url={this.state.url}/>
</div>
)
}
}
const Workers = (props) => {
return (
<div>
<button onClick={() => props.handleClick("http://localhost:5000/api")}>Worker 1</button>
<button onClick={() => props.handleClick("http://localhost:2000/api")}>Worker 2</button>
</div>
)
}
export default App
here is hermesworker.js
class HermesWorker extends Component {
constructor() {
super()
this.state = {
items: [],
visited: [{name: "This Drive", path: "#back", root: ""}]
}
this.handleFolderClick = this.handleFolderClick.bind(this)
this.handleFileClick = this.handleFileClick.bind(this)
}
componentDidMount() {
if (this.props.url.length === 0) return
fetch(this.props.url)
.then(res => res.json())
.then(items => this.setState({ items }))
}
render() {
const folders = this.state.items.map((item) => {
if (!item.isfile) {
return <Card handleClick={this.handleFolderClick} root={item.root} path={item.path} isfile={item.isfile} name={item.name} size={item.size}/>
}
})
const files = this.state.items.map((item) => {
if (item.isfile) {
return <Card handleClick={this.handleFileClick} root={item.root} path={item.path} isfile={item.isfile} name={item.name} s ize={item.size}/>
}
})
const pathButtons = this.state.visited.map((item) => {
return <PathButton handleClick={this.handleFolderClick} root={item.root} path={item.path} name={item.name}/>
})
return (
<div>
{pathButtons}
<div className="flex-container">
{folders}
{files}
</div>
</div>
)
}
}
Essentially the issue is that the HermesWorker component is not being rerendered to use the new url prop. I am not sure why this is happening because for example, in the hermesworker it renders other subcomponents that do get rerendered during a state change.
Any information is appreciated
EDIT updated to add hermes worker, the file is over 100 lines so i cut out and only pasted the stuff I thought was important to the issue, can supply more if needed
I tested that code and it seems to be working fine. Could you provide What is set in HermesWorker component?
Edit: You'll require to set your state with setState on component updates. To do this, you may look for componentDidUpdate, which will run on every update. This is different from componentDidMount, which (hopefully) will run once and then the component may update and re-render, but re-render it's not considered as "mount". So you may try this instead:
constructor(props) {
super(props);
this.state = {
url: '',
items: [],
visited: [{name: "This Drive", path: "#back", root: ""}]
}
this.fetchData = this.fetchData.bind(this);
}
componentDidMount() {
//Mount Once
}
componentDidUpdate(prevProps, prevState) {
if (this.state.url !== this.props.url) {
this.setState({url: this.props.url});
// Url state has changed.
}
if(prevState.url !== this.state.url){
//run your fetch
this.fetchData();
}
}
fetchData(){
if (this.props.url.length === 0) return
fetch(this.props.url)
.then(res => res.json())
.then(items => this.setState({ items }));
}
Note: I moved the fetch to its own function, but that's completly up to you.
Also notice i added url to the state. Make sure to keep your props set to avoid unexpected behaviours.
Edit 2: componentDidUpdate will hand you prevProps and prevState as parameters. With prevProps you get access to whatever props you got on the previous update, and with prevState, as you may guess, you get access to whatever-your-state-was on the previous update. And by "on the previous update" i mean before the update got executed.

React: How to update the DOM with API results

My goal is to take the response from the Google API perspective and display the value into a div within the DOM.
Following a tutorial : https://medium.com/swlh/combat-toxicity-online-with-the-perspective-api-and-react-f090f1727374
Form is completed and works. I can see my response in the console. I can even store the response into an object, array, or simply extract the values.
The issue is I am struggling to write the values to the DOM even though I have it saved..
In my class is where I handle all the API work
class App extends React.Component {
handleSubmit = comment => {
axios
.post(PERSPECTIVE_API_URL, {
comment: {
text: comment
},
languages: ["en"],
requestedAttributes: {
TOXICITY: {},
INSULT: {},
FLIRTATION: {},
THREAT: {}
}
})
.then(res => {
myResponse= res.data; //redundant
apiResponse.push(myResponse);//pushed api response into an object array
console.log(res.data); //json response
console.log(apiResponse);
PrintRes(); //save the values for the API for later use
})
.catch(() => {
// The perspective request failed, put some defensive logic here!
});
};
render() {
const {flirty,insulting,threatening,toxic}=this.props
console.log(flirty); //returns undefined, makes sense upon initialization but does not update after PrintRes()
return (
<div className="App">
<h1>Please leave a comment </h1>
<CommentForm onSubmit={this.handleSubmit} />
</div>
);
}
}
When I receive a response from the API I use my own function to store the data, for use later, the intention being to write the results into a div for my page
export const PrintRes=() =>{
// apiResponse.forEach(parseToxins);
// myResponse=JSON.stringify(myResponse);
for (var i = 0; i < apiResponse.length; i++) {
a=apiResponse[i].attributeScores.FLIRTATION.summaryScore.value;
b=apiResponse[i].attributeScores.INSULT.summaryScore.value;
c=apiResponse[i].attributeScores.THREAT.summaryScore.value;
d=apiResponse[i].attributeScores.TOXICITY.summaryScore.value;
}
console.log("hell0");//did this function run
// render(){ cant enclose the return in the render() because I get an error on the { , not sure why
return(
<section>
<div>
<p>
Your comment is:
Flirty: {flirty}
</p>
</div>
<div>
<p>
Your comment is:
insulting: {insulting}
</p>
</div>
<div>
<p>
Your comment is:
threatening: {threatening}
</p>
</div>
<div>
<p>
Your comment is:
toxic: {toxic}
</p>
</div>
</section>
);
}
Variables and imports at the top
import React from "react";
//needed to make a POST request to the API
import axios from "axios";
import CommentForm from "../components/CommentForm";
var myResponse;
var apiResponse= [];
let a,b,c,d;
let moods = {
flirty: a,
insulting:b,
threatening:c,
toxic:d
}
If I understand correctly You need to create a state where you store data from api.
States in react works like realtime stores to refresh DOM when something change. this is an example to use it
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
apiData: undefined
};
}
fetchData() {
this.setState({
apiData: "Set result"
});
}
render() {
const { apiData } = this.state;
return (
<div>
<button onClick={this.fetchData.bind(this)}>FetchData</button>
<h3>Result</h3>
<p>{apiData || "Nothing yet"}</p>
</div>
);
}
}
you can check it here: https://codesandbox.io/s/suspicious-cloud-l1m4x
For more info about states in react look at this:
https://es.reactjs.org/docs/react-component.html#setstate

'You tried to redirect to the same route you're currently on: "/"' when using a Redirect component with state

I'm trying to create a small demo application with two pages, one and two. The user may navigate from page one to page two by pressing a button, but only if the location objects' state in the second page contains a key attribute. If it does not, then the user is redirected to one.
The problem I'm encountering is that when using a to object to redirect from one and pass state to two, React Router warns:
You tried to redirect to the same route you're currently on: "/"
This doesn't make sense to me, because I'm attempting to redirect the user from /one to /two, not / to /two.
App.js
import React, { Component } from 'react';
import './App.css';
import { NavLink, Redirect, Route, BrowserRouter as Router, Switch } from 'react-router-dom';
const App = () => (
<Router>
<div className="App">
<ul>
<li>
<NavLink to="/one">One</NavLink>
</li>
<li>
<NavLink to="/two">Two</NavLink>
</li>
</ul>
<Switch>
<Route path="/one" component={One} />
<Route path="/two" component={Two} />
</Switch>
</div>
</Router>
);
class One extends Component {
constructor(props) {
super(props);
this.state = {
shouldRedirect: false,
};
this.redirect = this.redirect.bind(this);
}
redirect() {
this.setState({
shouldRedirect: true,
});
}
render() {
const { shouldRedirect } = this.state;
if (shouldRedirect) {
return (
// Replacing the below Redirect with the following corrects the error,
// but then I'm unable to pass state to the second page.
// <Redirect to="/two" />
<Redirect
to={{
pathName: '/two',
state: {
key: 'Some data.',
},
}}
/>
);
}
return (
<div>
<h3>This is the first page.</h3>
<button type="button" onClick={this.redirect}>Click me to go to page two.</button>
</div>
);
}
}
class Two extends Component {
constructor(props) {
super(props);
this.state = {
shouldRedirect: false,
};
this.redirect = this.redirect.bind(this);
}
componentWillMount() {
const { location } = this.props;
if (location.state && location.state.key) {
const { key } = location.state;
this.key = key;
} else {
this.redirect();
}
}
redirect() {
this.setState({
shouldRedirect: true,
});
}
render() {
const { shouldRedirect } = this.state;
if (shouldRedirect) {
return (
<div>
<p>You&apos;re being redirected to the first page because a key was not provided.</p>
<Redirect to="/one" />
</div>
);
}
return (
<div>
<h3>This is the second page.</h3>
<p>The key you provided was "{this.key}".</p>
</div>
);
}
}
export default App;
App.css
.App {
text-align: center;
}
You are passing pathName instead of pathname. This should fix the issue.
Working example on code sandbox.
<Redirect
to={{
pathname: "/two",
state: {
key: "Some data."
}
}}
/>
You should not use the <Redirect> as an another route in the <switch>. It should be condition driver. Use the below link for the reference.
https://reacttraining.com/react-router/web/example/auth-workflow

Resources