Why is this react component not displaying content loaded? - node.js

I'm working on this project and I want to display the content I got from the backend routes via axios to Showcase component. But the code doesn't give the output as expected the updated state console.log(cont) is working and no issue but it doesn't rendering contents.The app.js state is received by the component. I want to display the names. The child functional component as follows.
import React from 'react';
import {
Table,
Button
} from 'reactstrap';
function Showcase(props) {
const title = props.title;
const contents = props.contents;
let items_body = [];
items_body = contents.map(cont => {
console.log(cont)
if(cont.category === 'Men') {
return (
<div className="item_card" key={cont._id}>
<div className="itemC_right">
<div className="itemCR_topA">
<div className="itemCR_topA_title">{cont.name}</div>
</div>
</div>
</div>
)
}
else if(cont.category === 'Women') {
return (
<div className="lead content d-flex d-flex justify-content-center mb-3" key={cont.id}>
<div>Name : {cont.name}</div>
</div>
)
}
else if(cont.category === 'Kids') {
return (
<div className="lead content d-flex d-flex justify-content-center mb-3" key={cont.id}>
<div>Name : {cont.name}</div>
</div>
)
}
else
return (
null
)
})
return (
<div id="showcase">
<div id="showcase_card">
<div className="row">
<div className="col-sm-6 d-flex flex-row mt-1">
<h1 className="display-3 txt_secondary text-left" id="showcase_title">{title}</h1>
</div>
<div className="col-sm-6 d-flex flex-row-reverse mt-4">
<small className="txt_secondary text-right">Oreo is a online shopping store made just for you.</small>
</div>
</div>
<div>
{items_body}
</div>
</div>
</div>
)
}
export default Showcase;
The App.js class component
class App extends React.Component {
state = {
title: 'Oreo',
contents: []
}
changeState = (category,data) => {
this.setState({
title: category,
contents: data
})
}
handleNavigation = (e) => {
const option = e.target.innerHTML;
switch(option) {
case "Men":
axios.get('/api/items/men/2')
.then(res => {
this.changeState('Men',res.data);
// console.log(res.data)
})
break;
}
}
render() {
return (
<div>
<NavigationBar handleNavigation={this.handleNavigation} />
<Showcase title={this.state.title} contents={this.state.contents} />
<ItemWindow />
<BottomBar />
</div>
);
}
}
export default App;
In NavigationComponent.js when clicked on Men I'm sending it to App.js then it handles the click event. Why doesn't Showcase.js cannot show/render results? Help.

So I got rid of the if statements under the Showcase.js and could get my results. It seems that I was checking the category twice(in handleNavigation and here). I also added async and await as tonkalata's way.

Related

When reload the page all data disappears in the page in ReactJS

Uncaught TypeError: Cannot read properties of null (reading 'imageLink')
The above error occurred in the <ProductMoreDetails> component:
This Products page Display all the products in the database. When I click on any product card it will redirects to the more details page of that product. First time it loads without any issue and when I refresh the page or backward the page it will disappear all the content in the page.
ProductMoreDetails.js
import Navbar from "../components/Navbar";
import { useProductsContext } from "../hooks/useProductsContext";
import { useParams } from "react-router-dom";
import { useEffect } from "react";
//setproducts
const ProductMoreDetails = () => {
const {products,dispatch} = useProductsContext();
const {id} = useParams();
useEffect(() => {
const fetchProducts = async () => {
const response = await fetch(`/api/products/${id}`)
const json = await response.json()
if (response.ok) {
dispatch({type: 'SET_PRODUCTS', payload: json})
}
}
fetchProducts()
},[dispatch,id])
return (
<div className="moredetails">
<Navbar/>
<br /><br />
<div className="details">
<img className="productImage" src={products.imageLink} alt="productImage" />
<div className="details-section"></div>
<div className="row section1">
<div className="col-lg-8">
</div>
<div className="col-lg-2">
<button className="btn btn-primary addnew">Add to Cart</button>
</div>
</div>
<div className="row section1">
<div className="col-lg-8">
<h1>{products.productName}</h1>
</div>
<div className="col-lg-2">
<h1>Rs. {products.price}</h1>
</div>
</div>
<div className="section1">
<hr />
<h1>Product Details</h1>
<br />
<div className="row">
<div className="col-lg-6">
<div className="row">
<div className="col-lg-6">
<h6>Category</h6>
</div>
<div className="col-lg-6">
<p>{products.category}</p>
</div>
</div>
</div>
<div className="col-lg-6">
<div className="row">
<div className="col-lg-6">
<h6>Brand</h6>
</div>
<div className="col-lg-6">
<p>{products.brand}</p>
</div>
</div>
</div>
</div>
<br />
<div className="row">
<div className="col-lg-6">
<div className="row">
<div className="col-lg-6">
<h6>Model</h6>
</div>
<div className="col-lg-6">
<p>{products.model}</p>
</div>
</div>
</div>
<div className="col-lg-6">
<div className="row">
<div className="col-lg-6">
<h6>Year</h6>
</div>
<div className="col-lg-6">
<p>{products.year}</p>
</div>
</div>
</div>
</div>
<br />
<div className="row">
<div className="col-lg-6">
<div className="row">
<div className="col-lg-6">
<h6>Features</h6>
</div>
<div className="col-lg-6">
<p>{products.features}</p>
</div>
</div>
</div>
</div>
<hr />
<div className="section2">
<h1>Product Description</h1>
</div>
<p>
{products.description}
</p>
</div>
</div>
</div>
)
}
export default ProductMoreDetails;
ProductContext.js
import { createContext } from "react";
import { useReducer } from "react";
export const ProductsContext = createContext();
export const productsReducer = (state, action) => {
switch (action.type) {
// set products
case "SET_PRODUCTS":
return {
...state,
products: action.payload,
}
//set a single product
case "SET_PRODUCT":
return {
...state,
products: action.payload,
}
case 'CREATE_PRODUCT':
return {
products: [action.payload, ...state.products]
}
case 'DELETE_PRODUCT':
return {
products: state.products.filter((product) => product._id !== action.payload._id)
}
case 'UPDATE_PRODUCT':
return {
products: state.products.map((product) => product._id === action.payload._id ? action.payload : product)
}
default:
return state;
}
}
export const ProductsContextProvider = ({ children }) => {
const [state, dispatch] = useReducer(productsReducer, {
products: null
})
return (
<ProductsContext.Provider value={{...state, dispatch}}>
{ children }
</ProductsContext.Provider>
)
}

React How to get information from Get statement

Hello I'm building a pokedex and now I'm trying to get the pokemon information that was clicked.
so I get the URl
http://localhost:3000/#/pokemon/Bulbasaur
an trying to recor the Bulbasur information to display it:
import React, { Component } from 'react';
import Pokemons from '../pokemon/pokedex.json';
class PokePath extends Component {
constructor(props) {
super(props);
}
render() {
const {id, num, name} = this.state
return (
<div className="row">
<div className="col">
<h1>{Pokemons[this.props.id-1].name}</h1>
</div>
</div>
);
}
}
export default PokePath;
This where I'm getting the data:
[{
"id": 1,
"num": "001",
"name": "Bulbasaur",
"img": "http://www.serebii.net/pokemongo/pokemon/001.png",
"type": [
"Grass",
"Poison"
],
]
In order to get to the bulbasaur page I did this:
return (
<div className = "row">{Pokemons.map((postDetail, id) => {
return(
<div className = "col-md-3 col-sm-6 mb-5">
<StyledLink to={`pokemon/${postDetail.name}`}>
<div className = "card">
<div className = "card-header">
<div>
<a key={id}>{postDetail.id}</a>
</div>
<div className="text-center">
<img src={postDetail.img} alt="new"/>
</div>
<div className="text-center">
<a key={id}>{postDetail.name}</a>
</div>
</div>
</div>
</StyledLink>
</div>);
})}
</div>
);

How to display value in real time without refresh page with React and SocketIO?

I develop a basic application with NodeJS, React and SocketIO.
My NodeJS server sends socket to the React clients with a table of players (string value). I want display this table of players in the react view, and refresh it dynamically when it changes.
I tried some solutions but nothing works great. Have you ideas to do that or to improve my code ?
Thanks
Constructor : this.players[]
constructor(props){
super(props);
this.state = {
endpoint: "http://127.0.0.1:8080",
}
this.gameId = this.props.match.params.id;
this.players = [];
}
showPlayer : display list of players with cards
showPlayers = () => {
const classes = this.props;
let playersCards = [];
console.log(this.players);
this.players.foreach(function(p){
playersCards.push(
<Card className={classes.card}>
<CardHeader
avatar={
<Avatar style={{backgroundColor: "#00FF00"}} aria-label="Recipe">
R
</Avatar>
}
action={
<IconButton>
<MoreVertIcon />
</IconButton>
}
title={p}
subheader=""
/>
</Card>
)
}
return playersCards;
}
Socket.io : get the table of players updated
socket.on('ack-join-game', function(res){
this.players = res.dataGame.players;
});
Render :
const classes = this.props;
return(
<div className="GameConfig">
<h1>Salon de jeu</h1>
<div className="well" style={this.wellStyles}>
<h2>Informations</h2>
Id : {this.gameId}
<br></br>
<h2>Players (0/2)</h2>
<div id="cards">
</div>
{this.showPlayers()}
<form onSubmit={this.handleFormSubmit}>
<br></br>
<Button bsStyle="primary" type="submit" bsSize="large" block>
Lancer la partie
</Button>
</form>
</div>
<ToastContainer store={ToastStore}/>
</div>
)
}
You should store your players in the state of your component as changing them affects what is going to be rendered. Also, you can remove the endpoint if it is never going to change at runtime :
constructor(props){
super(props);
this.state = {
players = [],
}
this.gameId = this.props.match.params.id;
this.endpoint = "http://127.0.0.1:8080";
}
Then call setState to update players and refresh the component in your socket event :
socket.on('ack-join-game', res => {
this.setState({ players: res.dataGame.players })
});
Now, your players will need to be accessed via this.state.players instead of this.players.
You could also completely remove your showPlayers function using map:
const { players } = this.state
const { card } = this.props.classes
return (
<div className="GameConfig">
<h1>Salon de jeu</h1>
<div className="well" style={this.wellStyles}>
<h2>Informations</h2>
Id : {this.gameId}
<br></br>
<h2>Players (0/2)</h2>
<div id="cards">
</div>
{players.map(player =>
<Card className={card} key={player}>
<CardHeader
avatar={
<Avatar style={{ backgroundColor: "#00FF00" }} aria-label="Recipe">
R
</Avatar>
}
action={
<IconButton>
<MoreVertIcon />
</IconButton>
}
title={player}
subheader=""
/>
</Card>
)}
<form onSubmit={this.handleFormSubmit}>
<br></br>
<Button bsStyle="primary" type="submit" bsSize="large" block>
Lancer la partie
</Button>
</form>
</div>
<ToastContainer store={ToastStore} />
</div>
)

ReduxForm handleSubmit refreshes page with fields assigned

Environment
ReduxForm: v6.5.0
Node: v8.1.2
Browser: Google Chrome
I've gone through all the existing issues on handleSubmit page refreshing, but none of them seems to solve my problem.
LoginForm
import React, { Component } from 'react'
import { reduxForm, Field, propTypes } from 'redux-form'
import classNames from 'classnames'
import loginValidation from './validation'
#reduxForm({
form: 'loginForm',
validate: loginValidation
})
export default class LoginForm extends Component {
static propTypes = {
...propTypes
}
inputField = ({ input, label, type, meta: { touched, error } }) => (
<fieldset className="form__fieldset login-form__fieldset">
<div className="form__field">
<input {...input}
type={type}
placeholder={touched && error ? error : label}
className={classNames('form__input login-form__input',
touched && error ? 'ng-invalid' : ''
)}
/> {/* .ng-invalid */}
</div>
{type === 'password' &&
<div className="form__helper login-form__helper">
<div className="small">
<a>I've forgotten my password</a>
</div>
</div>
}
</fieldset>
)
render() {
const { inputField, props } = this
const { handleSubmit, submitting } = props
return (
<div className="habbo-login-form">
<form className="login-form__form" onSubmit={handleSubmit}>
<Field name="username" type="text" component={inputField} label="Username" />
<Field name="password" type="password" component={inputField} label="Password" />
<button className="login-form__button" type="submit" disabled={submitting}>Let's go!</button>
</form>
<div className="login-form__social">
<div className="habbo-facebook-connect" type="large">
<button className="facebook-connect">Login with Facebook</button>
</div>
<div className="habbo-facebook-connect" type="small">
<button className="facebook-connect"></button>
</div>
</div>
</div>
)
}
}
And the HOC LoginHeader:
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import classNames from 'classnames'
import LoginForm from './LoginForm'
import { authActions } from '../redux/modules/auth'
#connect(
state => ({
user: state.auth.user,
...state.form.loginForm
}),
{ ...authActions }
)
export default class LoginHeader extends Component {
static propTypes = {
user: PropTypes.object,
login: PropTypes.func.isRequired,
logout: PropTypes.func.isRequired
}
static contextTypes = {
router: PropTypes.object
}
onSubmit = (data) => this.props.login(data).then(console.log)
render() {
const { submitFailed } = this.props
const headerLoginForm = classNames(
'header__login-form',
submitFailed ? 'animated shake' : ''
)
return (
<div className="header__top sticky-header sticky-header--top">
<div className="wrapper">
<div className="header__top__content">
<div className={headerLoginForm}>
<LoginForm onSubmit={this.onSubmit} />
</div>
</div>
</div>
</div>
)
}
}
Even when I try to replace <form onSubmit={handleSubmit}> with <form onSubmit={(e) => e.preventDefault()}> my page still refreshes.
I'm unsure whether or not this is a problem with my coding, browser, version, or whatever else because practically yesterday it worked without any problem.

Jest snapshots testing

I use jest snapshot testing for one of my component and the generated snap file is huge (199Kb and 4310 lines). All snapshot file is print to the console (that's 3-4 secs of rendering) when the snapshot test fails and it gave me this "you're doing something wrong" feeling.
So my question is : Am i using snapshot testing correctly ?
component code :
import _ = require('lodash');
import React = require('react');
import {TranslatedMessage} from 'translator';
import {UserProfile} from './user-profile';
import {ICustomerProfile} from '../customer/customer-profile';
interface IUserProfile {
firstName: string;
lastName: string;
id: string;
customer: ICustomerProfile;
job: string;
email: string;
contacts: string;
phoneNumber: string;
}
interface IUserProfileProps {
contact: IUserProfile;
}
interface IUserProfileState {}
export class UserProfile extends React.Component<IUserProfileProps, IUserProfileState> {
constructor(props: IUserProfileProps) {
super(props);
}
public render(): JSX.Element {
return (
<div className="ext-admin-user-infos-details">
<div className="ext-admin-user-infos-details-content">
<div className="row">
<div className="col-md-12">
<h3>{this.props.contact.firstName } {this.props.contact.lastName}</h3>
<p className="ext-subtitle">
<span className="ext-minor">{this.props.contact.id}</span>
</p>
</div>
</div>
<div className="row">
<div className="col-md-8">
<div className="ext-admin-user-infos-card">
<h6>
<TranslatedMessage messageKey="common.labels.customer" />
</h6>
<ul>
<li>{this.props.contact.customer.name}</li>
</ul>
</div>
<div className="ext-admin-user-infos-card">
<h6>
<TranslatedMessage messageKey="admin.contact.infos.job" />
</h6>
<ul>
<li>{this.props.contact.job}</li>
</ul>
</div>
<div className="ext-admin-user-infos-card">
<h6>
<TranslatedMessage messageKey="admin.contact.infos.email" />
</h6>
<ul>
<li>{this.props.contact.email}</li>
</ul>
</div>
</div>
<div className="col-md-4">
<div className="ext-admin-user-infos-card">
<h6>
<TranslatedMessage messageKey="common.labels.followed" />
</h6>
<ol>
{this.renderContacts(this.props.contact.contacts)}
</ol>
</div>
<div className="ext-admin-user-infos-card">
<h6>
<TranslatedMessage messageKey="common.labels.phone" />
</h6>
<ul>
<li>{this.props.contact.phoneNumber}</li>
</ul>
</div>
</div>
</div>
</div>
</div>
);
}
protected renderContacts(contacts: IUserProfile[]): JSX.Element[] {
let contacts= [];
if (sales) {
_.map(sales, function(contact: IUserProfile): void {
salesContact.push(
<li>
{ contact.firstName}
{ contact.lastName}
</li>
);
});
}
return contacts;
}
}
And the test file
jest.mock('TranslatedMessage');
import React = require('react');
import {render} from 'enzyme';
import {user} from '../../../tests/tools';
import {UserProfile} from '../../../app/components/user-profile/user-profile';
describe('UserProfile', () => {
it('should match the snapshot', () => {
const tree = render(<UserProfile user={user} />);
expect(tree).toMatchSnapshot();
});
});
Trust that feeling.
You're using snapshot testing correctly, but you've reached the point where you need to break down large components into smaller components. Breaking them apart will allow you to mock the children components, which will cut down on your snapshot size (per snapshot, not in aggregate) and make your diffs easier to see and fix.
For example, instead of:
export class UserProfile extends Component {
public render() {
return (
<div className="ext-admin-user-infos-details">
<div className="ext-admin-user-infos-details-content">
<div className="row">
<div className="col-md-12">
<h3>{this.props.contact.firstName } {this.props.contact.lastName}</h3>
<p className="ext-subtitle">
<span className="ext-minor">{this.props.contact.id}</span>
</p>
</div>
</div>
// ...
</div>
</div>
)
}
}
You do:
export class UserProfile extends Component {
public render() {
return (
<div className="ext-admin-user-infos-details">
<div className="ext-admin-user-infos-details-content">
<div className="row">
<div className="col-md-12">
<UserProfileName
first={this.props.contact.firstName}
last={this.props.contact.firstName}
contactId={this.props.contact.id}
/>
</div>
</div>
// ...
</div>
</div>
)
}
}
export class UserProfileName extends Component {
public render() {
return (
<div>
<h3>{this.props.contact.first} {this.props.contact.last}</h3>
<p className="ext-subtitle">
<span className="ext-minor">{this.props.contact.contactId}</span>
</p>
</div>
);
}
}
Notice that I've moved the logic for rendering the user's name to another component. Here's the updated test, mocking the child component:
jest.mock('TranslatedMessage');
jest.mock('UserProfileName'); // Mock this and other children
import React = require('react');
import {render} from 'enzyme';
import {user} from '../../../tests/tools';
import {UserProfile} from '../../../app/components/user-profile/user-profile';
describe('UserProfile', () => {
it('should match the snapshot', () => {
const tree = render(<UserProfile user={user} />);
expect(tree).toMatchSnapshot();
});
});
The snapshot for this will be much smaller than if it was all in one component. Of course you would have tests for the children components as well:
import React = require('react');
import {render} from 'enzyme';
import {UserProfileName} from '../../../app/components/user-profile/user-profile-name';
describe('UserProfileName', () => {
it('should match the snapshot with all props', () => {
const tree = render(<UserProfile first="Test" last="Testerson" contactId="test-id" />);
expect(tree).toMatchSnapshot();
});
it('should render without a first name', () => {
const tree = render(<UserProfile last="Testerson" contactId="test-id" />);
expect(tree).toMatchSnapshot();
});
it('should render without a last name', () => {
const tree = render(<UserProfile first="Test" contactId="test-id" />);
expect(tree).toMatchSnapshot();
});
});
Notice that in these tests I added two more cases at the end. When you break components down like this, it's a lot easier to understand and test for specific use-cases of the children components!
Finally, an added benefit of this approach is that now you have a re-usable component that knows how to render a user name! You could generalize this and plop it in whenever you need it.
You're doing the testing right, but you should definitely divide your component into multiple smaller ones, as it's too big at the moment.
When using enzyme, you might not want always to use render for testing, as it's what makes an output this big. When snapshot testing pure components, for example, you should use shallow.
We're using react-test-rendered for snapshot testing, it's more lightweight than enzyme.
If you're using enzyme, you should be using a shallow render.
import { shallow } from 'enzyme';
const component = shallow(<UserProfile user={user} />);
expect(component.text()).toMatchSnapshot();
You can also use react-test-renderer as well:
import renderer from 'react-test-renderer';
const component = renderer.create(<UserProfile user={user} />);
const tree = component.toJSON();
expect(tree).toMatchSnapshot();

Resources