After I submit my form the component does not update to render the new information - node.js

I have a component with a forum (redux-form) that adds a food to the data. After I click "ADD" the information I add in the form is added to the database, however, the component that renders the list of food does not update automatically unless I manually refresh the page. Then the new food is rendered in the component.
Not sure why this is not rendering the new information automatically
Stock.js (where the food list is rendered)
import _ from 'lodash';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchFoodList, addToPot, deleteFood } from '../actions';
import Header from './header';
import Pot from './pot';
import AddFood from './add_food';
import RecipeList from './recipe_list';
import classes from "../index.css";
import { Row, Col } from 'react-flexbox-grid';
class FoodList extends Component {
componentDidMount () {
this.props.fetchFoodList();
}
addIngredientToPot = (ev) => {
const val = ev.target.dataset.value;
const newPot = [ ...this.props.pot,
val
];
this.props.addToPot(newPot)
}
onDeleteClick = (ev) =>{
// const {id} = this.props.match.params;
const val = ev.target.dataset.value;
this.props.deleteFood(val);
}
displayFoodList() {
return _.map(this.props.foods, food => {
return (
<li key={food._id} className={classes.tablerow}>
<div data-value={food.name}
onClick={this.addIngredientToPot.bind(this)}
className={classes.col1}>{food.name}
</div>
<div className={classes.col2}>{food.type}</div>
<div className={classes.col3}>
<button data-value={food._id}
onClick={this.onDeleteClick.bind(this)}
className={classes.throwoutbutton}
> Throw Out
</button>
</div>
</li>
);
});
}
render () {
// console.log(this.props.foods);
// console.log(this.props.pot)
return (
<div className={classes.stockpilecontainer}>
<Header />
<Row>
<Col lg >
<h2>StockPile</h2>
<ul className={classes.responsivetable}>
<li className={classes.tableheader}>
<div className={classes.col1}>Name</div>
<div className={classes.col2}>Type</div>
<div className={classes.col3}>Throw Out</div>
</li>
{this.displayFoodList()}
</ul>
<div>
<AddFood/>
</div>
</Col>
<Col/>
<Pot/>
<Col/>
</Row>
<Row>
<Col xs={12}>
<Row center="xs">
<RecipeList xs={6} />
</Row>
</Col>
</Row>
</div>
);
};
}
function mapStateToProps(state) {
return {
foods: state.foods,
pot: state.pot.pot,
};
}
export default connect (mapStateToProps, { fetchFoodList, addToPot, deleteFood })(FoodList);
Newfood.js (component with form to add a new food item)
import React, { Component } from 'react';
import { Field, reduxForm } from 'redux-form';
import { connect } from 'react-redux';
import { addFood } from '../actions';
import classes from "../index.css";
import { Redirect } from 'react-router-dom';
const renderField= field => {
const { input, type } = field;
return (
<div className={classes.addfoodinput}>
<label className={classes.addfoodlabel}>{field.label}</label>
<input {...input} type={type} className="form-control" />
</div>
)
}
class AddFood extends Component {
onSubmit(values) {
this.props.addFood(values);
}
render () {
const { handleSubmit } = this.props;
return (
<form onSubmit={handleSubmit(this.onSubmit.bind(this))} className={classes.addfoodform}>
<Field
label="Food "
name="name"
component={renderField}
/>
<Field
label="Type "
name="type"
component={renderField}
/>
<button type="submit" className={classes.addfoodbutton} >ADD</button>
<button className={classes.addfoodbutton}>Cancel</button>
{/* <Redirect to={'/stockpile'} /> */}
</form>
)
}
};
AddFood = reduxForm({
form: 'NewFoodForm',
fields: ['name', 'type'],
})(AddFood);
export default connect(null, {addFood})(AddFood)
Reducer for the food list:
import _ from 'lodash';
import { FETCH_FOODLIST } from '../actions/types';
export default function(state = {}, action) {
switch (action.type) {
case FETCH_FOODLIST:
return _.mapKeys(action.payload.data, '_id');
default:
return state;
}
}
Action creators for adding new food and get the food list:
// Action to get the food list:
export const fetchFoodList = () => dispatch => {
Axios
.get(`${ROOT_URL}/myfoodlist`, {
headers: { auth: localStorage.getItem('token') }
})
.then( response =>
dispatch({
type: FETCH_FOODLIST,
payload: response
}));
};
// Add a food to the DB:
export function addFood (values, history) {
return function (dispatch) {
Axios.post(`${ROOT_URL}/myfoodlist/foods`, values)
.then( response => {
dispatch ({
type: ADD_NEW_FOOD,
payload: response
});
// history.push('/');
history.pushState(null, '/stockpile');
})
.catch( () => {
});
};
}

Related

Cannot set headers after they are sent to the client [NEXTJS]

I am currently busy working on a personal project with a dashboard using NextJS, which I believe allows me to learn NextJS and the fundementals of typescript. I am trying to work out how to set a welcome message if a response sent back from the server is set to True, I did manage to code this in, but I got cannot set headers after they are sent to the client.
My entire code file for the dashboard is the following snippet:
import styles from "./index.module.css";
import { type NextPage } from "next";
import Head from "next/head";
import Link from "next/link";
import Container from 'react-bootstrap/Container';
import Nav from 'react-bootstrap/Nav';
import Navbar from 'react-bootstrap/Navbar';
import NavDropdown from 'react-bootstrap/NavDropdown';
import { signIn, signOut, useSession } from "next-auth/react";
import Image from 'react-bootstrap/Image'
import Router from "next/router";
import { useEffect, useState } from "react";
import { library } from '#fortawesome/fontawesome-svg-core'
import { faHouse, fas, faServer } from '#fortawesome/free-solid-svg-icons'
import "#fortawesome/fontawesome-svg-core/styles.css";
import { Offline, Online } from "react-detect-offline";
import { config } from "#fortawesome/fontawesome-svg-core";
// Tell Font Awesome to skip adding the CSS automatically
// since it's already imported above
config.autoAddCss = false;
{/* The following line can be included in your src/index.js or _App.js file*/}
import 'bootstrap/dist/css/bootstrap.min.css';
library.add(fas, faServer, faHouse)
// import the icons you need
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
const Protected: NextPage = () => {
const { status, data } = useSession()
const { data: sessionData2 } = useSession();
useEffect(() => {
if (status === "unauthenticated") Router.replace("/auth/signin");
}, [status]);
// const isBreakpoint = useMediaQuery(768)
// return (
// <div>
// { isBreakpoint ? (
// <div>
// <HamburgerMenu />
// </div>
// ) : (
// <div>
// <FullMenu />
// </div>
// )
if (status === "authenticated")
return (
<><Navbar bg="dark" expand="lg" variant="dark" className="justify-content-end flex-grow-1 pe-3">
<Container>
<Navbar.Brand href="#home" style={{ fontSize: '25px' }}><strong>Litika.</strong> </Navbar.Brand>
{/* <Navbar.Toggle aria-controls="basic-navbar-nav" /> */}
<Navbar.Collapse id="basic-navbar-nav" className={styles.rightNavbar}>
<Nav className={styles.rightNavbar}>
<Nav.Link href="#home" className={styles.body}><FontAwesomeIcon icon={faHouse} /></Nav.Link>
<Nav.Link href="#link" className={styles.body}>Link</Nav.Link>
<NavDropdown className={styles.body} title={<Image
src={sessionData2 ? `https://cdn.pixabay.com/photo/2015/10/05/22/37/blank-profile-picture-973460_640.png` : 'https://cdn.pixabay.com/photo/2015/10/05/22/37/blank-profile-picture-973460_640.png'}
roundedCircle
style={{ width: '30px' }} />}
id="basic-nav-dropdown">
<NavDropdown.Item href="#action/3.1" onClick={sessionData2 ? () => void signOut() : () => void signIn()}>
{sessionData2 ? "Sign out" : "Sign in"}
</NavDropdown.Item>
<NavDropdown.Item href="#action/3.2">
Another action
</NavDropdown.Item>
<NavDropdown.Item href="#action/3.3">Something</NavDropdown.Item>
<NavDropdown.Divider />
<NavDropdown.Item href="#action/3.4">
Separated link
</NavDropdown.Item>
</NavDropdown>
</Nav>
</Navbar.Collapse>
</Container>
</Navbar>
<div className={styles.connectivityStatus}>
<br></br>
<div className={styles.connectivityStatusBox}>
<Online>🟢 You are connected to the internet.</Online>
<Offline>🔴 You are not connected to the internet.</Offline>
</div>
<WelcomeContainer />
</div>
<main className={styles.main}>
<h1 className={styles.title}>
Litika. </h1>
<div className={styles.container}>
<div className={styles.cardRow}>
<div className={styles.card}>
<h1 className={styles.cardTitle}><FontAwesomeIcon icon={faServer} /> HELLO!</h1>
<p className={styles.cardText}>How are you? I am under the water.</p>
</div>
</div>
</div>
</main>
<main className={styles.main2}>
<div className={styles.container}>
</div>
</main>
</>
);
return (
<>
<main className={styles.main}>
<div className={styles.container}>
<h1 className={styles.title}>
Loading the Dashboard.
</h1>
</div>
</main>
<main className={styles.main2}>
<div className={styles.container}>
<h1 className={styles.title}>
</h1>
</div>
</main>
</>
);
};
export default Protected;
const WelcomeContainer: React.FC = () => {
const { data: sessionData } = useSession();
var [welcomeMessage, setWelcomeMessage] = useState(null);
const payload = JSON.stringify({
email: sessionData.user?.email,
});
fetch('/api/userService/isuserwelcome', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: payload
})
.then(response => response.json())
.then(data => {
console.log(data);
if (data === true) {
setWelcomeMessage(
<div className={styles.welcomeContainer}>
<h1>Welcome to the Los pollos hermanos family.</h1>
</div>
);
}
})
.catch(error => {
console.error(error);
});
return (
<>
{welcomeMessage}
</>
);
};
The code for the IsUserWelcome API Route is the following:
import type { NextApiRequest, NextApiResponse } from 'next';
import { PrismaClient } from '#prisma/client';
const prisma = new PrismaClient();
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
try {
const { email } = req.body;
const cliresponse = await prisma.user.findFirst({
where: {
email,
},
});
console.log(cliresponse.newUser)
if (cliresponse.newUser == true) {
res.status(200).json(cliresponse.newUser)
}
res.status(200).json({ cliresponse });
} catch (err) {
console.log(err)
res.status(500).json({ error: err});
}
}
I have identified the issue to be coming from the WelcomeContainer Functional Component and do understand that NextJS & React are dynamic, which will lead to updates through the DOM. However, I haven't really tried anything yet to fix this issue because nothing from doing a simple Google search could lead me to fix this issue, so any guidance & help will be appreciated!
The parts that are different in this question is that it pertains to react and NextJS in general, not express.
You are sending a response twice, after a response is send you should return from the function.
import type { NextApiRequest, NextApiResponse } from 'next';
import { PrismaClient } from '#prisma/client';
const prisma = new PrismaClient();
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
try {
const { email } = req.body;
const cliresponse = await prisma.user.findFirst({
where: {
email,
},
});
console.log(cliresponse.newUser)
if (cliresponse.newUser == true) {
res.status(200).json(cliresponse.newUser)
return; // you need to return here so the code below doesn't get executed
}
res.status(200).json({ cliresponse });
} catch (err) {
console.log(err)
res.status(500).json({ error: err});
}
}

how to test react custom hook function call on button click

I am having a problem testing the button click event. In test cases, toHaveBeenCalled returns 0.
Here below is my component file:
index.js (component file)
import { Button, ButtonThemes } from 'stitcht-ui';
import useReels from './hooks/useReels';
import { ReelCard } from '../../components/ReelCard';
import styles from './dashboard.module.scss';
import CreateReelButton from '../../components/CreateReelButton';
const Dashboard = () => {
const {
reels,
fetchMoreReels,
loading,
loadMoreRef,
allReelsLoaded,
threadDeleted,
} = useReels();
const navigate = useNavigate();
const reelOnClick = (reelId) => navigate(`/reel/${reelId}`);
if (!reels) {
return (
<div className={styles.reels}>
<ReelCard loading />
<ReelCard loading />
<ReelCard loading />
</div>
);
}
if (reels && reels.length === 0) {
return (
<div className={styles['empty-message-container']}>
<img src="/reel.svg" alt="No Reels" height="120" width="120" />
<p className={styles['empty-message-title']}>
You don&apos;t have any threads
</p>
<p className={styles['empty-message']}>
Create a thread to start a conversation with your communities.
</p>
<CreateReelButton label="CREATE A THREAD" variant="outlined" />
</div>
);
}
return (
<div id="dashboard">
<h2 className="title">Your Threads</h2>
<div className={styles.reels}>
{reels.map((reel) => (
<ReelCard
key={reel.id}
loading={false}
reel={reel}
onClick={() => reelOnClick(reel.id)}
onThreadDeleted={() => threadDeleted(reel)}
/>
))}
</div>
<div className={styles['load-more']} ref={loadMoreRef}>
<Button
theme={ButtonThemes.PRIMARY}
onClick={fetchMoreReels}
loading={loading}
disabled={allReelsLoaded || reels.length < 10}
>
{allReelsLoaded || reels.length < 10
? 'No more threads'
: 'Load More'}
</Button>
</div>
</div>
);
};
export default Dashboard;
index.test.tsx (test file)
import { when, resetAllWhenMocks, verifyAllWhenMocksCalled } from 'jest-when';
import { render, screen, waitFor, userEvent } from '../../../test-utils';
import Dashboard from '.';
import { UserContextValue } from '../../providers/auth';
import useReels from './hooks/useReels';
const userData = {
// dummy user data
};
const reelsData = {
reels: //reels array ,
loading: false,
loadMoreRef: { current: null },
allReelsLoaded: false,
fetchMoreReels: jest.fn(),
threadDeleted: jest.fn(),
};
jest.mock('../../../src/service/user');
jest.mock('../../../src/service/reel');
jest.mock('./hooks/useReels');
jest.mock('../../../src/providers/auth', () => ({
useCurrentUser: () =>
({
user: userData,
} as unknown as UserContextValue),
}));
afterEach(() => {
verifyAllWhenMocksCalled();
resetAllWhenMocks();
});
beforeEach(() => {
when(useReels).expectCalledWith().mockReturnValue(reelsData);
});
describe('Dashboard testing', () => {
test(`"Load more" button should be performed on click`, async () => {
render(<Dashboard />);
const loadMoreButton = screen.getByRole('button', {
name: 'Load More',
});
expect(loadMoreButton).toBeVisible();
await userEvent.click(loadMoreButton);
await waitFor(() => {
expect(jest.fn()).toHaveBeenCalled();
});
});
});
When toHaveBeenCalled always returning 0 but it should return 1.
Can someone tell me why this is return always 0 instead of 1?

Jest cannot read property of "searchForGames" of undefined

I am trying to create a test, that checks wether a component draws correctly or not. But when i try to run the test i get this error message:
TypeError: Cannot read property 'searchForGames' of undefined
I have tried adding som mock data in searchForGames, but i cant get it to work:(
Here is my code:
search.tsx
import * as React from "react";
import { Component } from "react-simplified";
import { CardGroup, GameCard, Container, NavBar, SubHeader, Alert } from "./widgets";
import { igdbService, Game } from "./services";
export class Search extends Component<{
match: { params: { searchString: string } };
}> {
games: Game[] = [];
render() {
return (
<Container>
{this.games.length !== 0 ? (
<CardGroup>
{this.games.map((game) => {
return (
<div key={game.id} className="col">
<NavBar.Link to={"/games/" + game.id}>
<GameCard
name={game.name}
url={game.cover ? game.cover.url.replace("thumb", "cover_big") : ""}
/>
</NavBar.Link>
</div>
);
})}
</CardGroup>
) : (
<div className="d-flex flex-column justify-content-center">
<h4 className="mx-auto">The game you are looking for could not be found in the IGDB database..</h4>
<img className="w-40 mx-auto" alt="Sadge" src="https://c.tenor.com/kZ0XPsvtqw8AAAAi/cat-farsi-sad.gif"/>
</div>
)}
</Container>
);
}
mounted() {
this.getGames();
}
getGames() {
igdbService
.searchForGames(this.props.match.params.searchString)
.then((response) => (this.games = response))
}
}
search.test.tsx
import {Search} from "../src/Search"
import * as React from "react";
import { shallow } from 'enzyme';
import { NavBar, GameCard } from "../src/widgets";
jest.mock("../src/services", () => {
class Service {
searchForGames(string: searchString) {
return Promise.resolve(
[
{
id:143755,
cover: {
id:1311803,
url: '//images.igdb.com/igdb/image/upload/t_thumb/co2tp7.jpg'
},
name: "Terrorist Killer",
}
]
)
}
getGames() {
return Promise.resolve();
}
}
return new Service();
})
test("Search draws correctly", (done) => {
const wrapper = shallow (<Search match={{params: {searchString: "Terrorist"} }}/>)
setTimeout(() => {
expect(
wrapper.containsAllMatchingElements([
<div>
<NavBar.Link to="/games/143755">
<GameCard
name="Terrorist Killer"
url="//images.igdb.com/igdb/image/upload/t_thumb/co2tp7.jpg"
/>
</NavBar.Link>
</div>
])
).toEqual(true);
done();
})
})

Not gettting all the data from the MongoDB

I have a route /courses and when I do search for courses from the frontend(ReactJs) it takes me to this URL http://localhost:3001/courses/search?searchQuery=introducton but when I again click on courses button(that is on my navbar), it takes me to /courses but It doesn't show all the courses, it only shows the searched query that I got from the above query. Every time I need to refresh the page to get all the courses.
Courses.js file
import React, { useState } from "react"
import CourseHeader from "./CourseHeader"
import CoursesList from "./CoursesList"
import FilterCourse from "./FilterCourse"
import Filtertech from "./Filtertech"
import { useDispatch, useSelector } from "react-redux"
import { useHistory } from "react-router"
import { getCoursesBySearch } from "../../actions/courses"
const Courses = () => {
const dispatch = useDispatch()
const [search, setSearch] = useState("")
const history = useHistory()
const searchCourses = () => {
if (search.trim()) {
dispatch(getCoursesBySearch({ search }))
history.push(`/courses/search?searchQuery=${search || "none"}`)
} else {
history.push("/courses")
}
}
const handleKeyPress = (e) => {
if (e.keyCode === 13) {
searchCourses()
}
}
const courses = useSelector((state) => state.courses)
return (
<div className="row-start-2 row-end-3 col-start-1 col-end-2 mx-auto w-full max-w-5xl ">
<div className="grid grid-cols-layout-course gap-x-3">
<div>
<FilterCourse />
<Filtertech />
</div>
<div>
<CourseHeader
handleKeyPress={handleKeyPress}
setSearch={setSearch}
search={search}
/>
<CoursesList courses={courses} />
</div>
</div>
</div>
)
}
export default Courses
CourseList.js
import React from "react"
import CourseCard from "./CourseCard"
const CoursesList = ({ courses }) => {
return (
<div className="">
{courses && courses.map((course) => <CourseCard data={course} key={course._id} />)}
</div>
)
}
export default CoursesList
App.js
import "./App.css"
import "./assets/output.css"
import { BrowserRouter as Router, Switch, Route } from "react-router-dom"
import Navbar from "./components/layouts/Navbar"
import Courses from "./components/courses/Courses"
import { useEffect } from "react"
import { getCourses } from "./actions/courses"
function App() {
const dispatch = useDispatch()
useEffect(() => {
dispatch(getCourses())
}, [dispatch])
return (
<Router>
<div className="App min-h-screen grid grid-rows-layout-desktop grid-cols-layout-desktop gap-10">
<Navbar />
<Switch>
<Route path="/courses">
<Courses />
</Route>
</Switch>
<Footer />
</div>
</Router>
)
}
export default App
Anyone please help me with this

How to verify the username in the url

I can just type any username in the url and the same component gets rendered with the logged-in user's posts. Like when I'm logged in as rick, I can see my feed as http://localhost:3000/profile/Rick/posts
but if I type the url with any string or number or anything, it still renders the component. Like http://localhost:3000/profile/xcxcxcxcx/posts, it still shows the same posts as Rick's feed.
I think I have to verify the username.
Here's my code.
UserProfile
import React, { Component } from "react"
import { getCurrentUser } from "../actions/userActions"
import { connect } from "react-redux"
import { Link } from "react-router-dom"
class UserProfile extends Component {
componentDidMount() {
const authToken = localStorage.getItem("authToken")
this.props.dispatch(getCurrentUser(authToken))
}
render() {
const { isIdentifyingToken, username, email } = this.props
return (
<div>
{isIdentifyingToken ? null : (
<div className="card">
<div className="card-content">
<div className="media">
<div className="media-left">
<figure className="image is-48x48">
<img
src="https://bulma.io/images/placeholders/96x96.png"
alt="Placeholder image"
/>
</figure>
</div>
<div className="media-content">
<p className="title is-4">{username}</p>
<p className="subtitle is-6">{email}</p>
</div>
</div>
<Link to={`/profile/${username}/posts`}>My Posts</Link>
<br></br>
</div>
</div>
)}
</div>
)
}
}
const mapStateToProps = state => {
return {
isIdentifyingToken: state.auth.isIdentifyingToken,
username: state.auth.user.username,
email: state.auth.user.email,
id: state.auth.user._id
}
}
export default connect(mapStateToProps)(UserProfile)
UserFeed
import React, { Component } from "react"
import { getUserPosts, getCurrentUser } from "../actions/userActions"
import { connect } from "react-redux"
import Cards from "./Cards"
class UserFeed extends Component {
componentDidMount() {
const authToken = localStorage.getItem("authToken")
if (authToken) {
this.props.dispatch(getCurrentUser(authToken))
if (this.props && this.props.userId) {
this.props.dispatch(getUserPosts(this.props.userId))
} else {
return null
}
}
}
render() {
const { isFetchingUserPosts, userPosts } = this.props
return isFetchingUserPosts ? (
<p>Fetching....</p>
) : (
<div>
{userPosts &&
userPosts.map((post) => {
return <Cards key={post._id} post={post} />
})}
</div>
)
}
}
const mapStateToPros = (state) => {
return {
isFetchingUserPosts: state.userPosts.isFetchingUserPosts,
userPosts: state.userPosts.userPosts,
userId: state.auth.user._id,
}
}
export default connect(mapStateToPros)(UserFeed)
Cards
import React, { Component } from "react"
import { connect } from "react-redux"
import { compose } from "redux"
import { withRouter } from "react-router-dom"
class Cards extends Component {
render() {
const {title, description } = this.props.post
return (
<div className="card">
<div className="card-content">
<div className="media">
<div className="media-left">
<figure className="image is-48x48">
<img
src="https://bulma.io/images/placeholders/96x96.png"
alt="Placeholder image"
/>
</figure>
</div>
<div className="media-content" style={{ border: "1px grey" }}>
<p className="title is-5">{title}</p>
<p className="content">{description}</p>
</div>
</div>
</div>
</div>
)
}
}
const mapStateToProps = state => {
return state
}
export default compose(withRouter, connect(mapStateToProps))(Cards)
I have a backend route which verifies the user like: (it just takes the decoded object out from the token which contains the userId and verifies the user)
identifyUser: async (req, res, next) => {
try {
const userId = req.user.userId
const user = await User.findOne({ _id: userId })
if (!user) {
return res.status(500).json( {error: "No user found "})
}
return res.status(200).json( { user })
} catch(error) {
return next(error)
}
}
I'm not sure how do I verify the username in the params:(
Edit: App.js
render() {
return (
<div>
<Router>
<Switch>
<Route exact path="/" component={LandingPage} />
<Route path="/register" component={RegistrationForm} />
<Route path="/login" component={LoginForm} />
<PrivateRoute path="/feed" component={Feed} />
<PrivateRoute path="/post/new" component={NewForm} />
<PrivateRoute path="/post/edit/:id" component={EditForm} />
<PrivateRoute exact path="/profile/:username" component={UserProfile} />
<PrivateRoute path="/profile/:username/posts" component={UserFeed} />
<Route component={NotFoundPage} />
</Switch>
</Router>
</div>
)
}
UserFeed is rendered with the component prop, so it receives the route props, match, location, and history. The match prop is the one you should be interested in.
this.props.match.params.username
UserFeed, however, needs to react to the match prop updating and refetch the posts. Here I believe I'm correctly assuming the path's username is the same as the user's userId property. (feel free to correct me if assumption is incorrect). This you'll need the componentDidUpdate lifecycle function.
componentDidUpdate(prevProps) {
const { match: { params: { username } } } = this.props;
const { match: { params: { username: prevUsername } } } = prevProps;
if (prevUsername !== username) {
// whatever other logic you need to validate user first
this.props.dispatch(getUserPosts(username));
}
}

Resources