Reactjs: React-router-dom Cannot Read Property Push of Undefined - javascript

Before you conclude that i have not combed stackoverflow very well, i will like to state that i have done that but i have not yet gotten the solution.
Good day guys, i have been having this issue for days, i was trying to push after clicking on a button but i cannot. I then tried to console.log this.props in the componentDidMount and it was showing an empty object.
This is the App.js file
import React, { Component } from 'react';
import { Route, Switch } from 'react-router-dom';
import Layout from './hoc/Layout/Layout';
import BurgerBuilder from './containers/BurgerBuilder/BurgerBuilder';
import Checkout from './containers/Checkout/Checkout';
class App extends Component {
render () {
return (
<div>
<Layout>
<Switch>
<Route path="/" exact render = {routeProps => (
<BurgerBuilder {...routeProps} />
)}
/>
<Route path="/checkout" exact component={Checkout} />
</Switch>
</Layout>
</div>
);
}
}
export default App;
This is the burger-builder component
import React, { Component } from 'react'
import Aux from '../../hoc/Aux_/Aux_'
import Burger from '../../components/Burger/Burger'
import BuildControls from '../../components/Burger/BuildControls/BuildControls'
import Modal from '../../components/UI/Modal/Modal'
import OrderSummary from '../../components/Burger/OrderSummary/OrderSummary'
import Spinner from '../../components/UI/Spinner/Spinner'
import withErrorHandler from '../../hoc/withErrorHandler/withErrorHandler'
import axios from '../../axios-orders'
const INGREDIENT_PRICES = {
salad: 0.5,
cheese: 0.4,
meat: 1.3,
bacon: 0.7
}
class BurgerBuilder extends Component{
constructor(props){
super(props);
this.state = {
ingredients:null,
totalPrice:4,
purchaseable:false,
purchasing:false,
loading:false,
error: false
}
}
componentDidMount(){
console.log(this.props)
axios.get('/ingredients')
.then(response => {
this.setState({ingredients : response.data});
})
.catch((error) => {
this.setState({error: true})
})
}
updatePurchaseState = (ingredients) => {
const sum = Object.keys(ingredients)
.map((igKey) => (
ingredients[igKey]
))
.reduce((sum, el) =>{
return sum + el
} ,0)
this.setState({purchaseable: sum > 0})
}
addIngredientHandler = (type) => {
const oldCount = this.state.ingredients[type];
const updatedCount = oldCount + 1;
const updateIngredients = {
...this.state.ingredients
};
updateIngredients[type] = updatedCount;
const priceAddition = INGREDIENT_PRICES[type];
const oldPrice = this.state.totalPrice
const newPrice = oldPrice + priceAddition;
this.setState({totalPrice: newPrice, ingredients: updateIngredients},
this.updatePurchaseState(updateIngredients)
)
}
removeIngredientHandler = (type) => {
const oldCount = this.state.ingredients[type];
if(oldCount <= 0){
return ;
}
const updatedCount = oldCount - 1;
const updateIngredients = {
...this.state.ingredients
};
updateIngredients[type] = updatedCount;
const priceDeduction = INGREDIENT_PRICES[type];
const oldPrice = this.state.totalPrice
const newPrice = oldPrice - priceDeduction;
this.setState({totalPrice: newPrice, ingredients: updateIngredients},
this.updatePurchaseState(updateIngredients)
)
}
purchaseHandler = () => {
this.setState({purchasing:true})
}
purchaseCancelHandler = () => {
this.setState({purchasing:false})
}
purchaseContinueHandler = () => {
// this.setState({loading:true})
// alert('You Continued')
// const order = {
// ingredients: this.state.ingredients,
// price: this.state.totalPrice,
// customer:{
// name:'Durojaye Peter',
// address:{
// street:'Ire-Akari Estate 2',
// zipCode: '41351',
// country: 'Nigeria'
// },
// email:'oluleyepeters#gmail.com',
// },
// deliveryMethod:'fastest'
// }
// axios.post('/orders',order)
// .then(response => {
// this.setState({loading:false, purchasing:false})
// })
// .catch(error => {
// this.setState({loading:false, purchasing:false})
// })
this.props.history.push('/')
}
render(){
const disabledInfo = {
...this.state.ingredients
};
for(let key in disabledInfo){
disabledInfo[key] = disabledInfo[key] <= 0
}
let orderSummary = null
let burger = this.state.error ? <p>Ingredients cannot be loaded</p> : <Spinner />
if(this.state.ingredients !== null){
burger = (
<Aux>
<Burger ingredients={this.state.ingredients}/>
<BuildControls
ingredientAdded={this.addIngredientHandler}
ingredientRemoved={this.removeIngredientHandler}
disabled={disabledInfo}
purchaseable={this.state.purchaseable}
price= {this.state.totalPrice}
ordered={this.purchaseHandler}
/>
</Aux>
);
orderSummary = <OrderSummary
ingredients={this.state.ingredients}
purchaseCanceled = {this.purchaseCancelHandler}
purchaseContinued = {this.purchaseContinueHandler}
price={this.state.totalPrice}
/>
}
if(this.state.loading){
orderSummary = <Spinner />
}
return(
<Aux>
<Modal show={this.state.purchasing} modalClosed={this.purchaseCancelHandler}>
{orderSummary}
</Modal>
{burger}
</Aux>
)
}
}
export default withErrorHandler(BurgerBuilder, axios)
As it is shown in the code,i was trying to console.log this.props in the purchaseContinueHandler but it keeps showing an empty object.
Thanks a lot guys any help will be appreciated.

Have you tried giving the basename to the Layout component. Also try removing Exact.
<Layout basename={process.env.REACT_APP_BASE_PATH}>
<Switch>
<Route path="/" render = {routeProps => (
<BurgerBuilder {...routeProps} />
)}
/>
</Layout

Related

React and Javascript: how can I move const out of function App()?

How can I move one or more const out of function App?
In the simple test App below, I'm using localStorage to store a value which determines if a div is dispayed. The handleToggle dismisses the div and stores a value in localStorage. Clearing localstorage and reloading shows the div again.
In a simple test App on localhost, this works. But in my more complex production App, I'm getting the error Invalid hook call. Hooks can only be called inside of the body of a function component , which has a myriad of fixes, one of which points out the issue may be that a const needs to be a separate function.
And so I'm thinking the issue is that I need to convert the two const to a function that can be placed right under the import blocks and out of the function App() block.
As a start, in this simple App, how can I move the two const out of function App()?
import './App.css';
import * as React from 'react';
function App() {
const [isOpen, setOpen] = React.useState(
JSON.parse(localStorage.getItem('is-open')) || false
);
const handleToggle = () => {
localStorage.setItem('is-open', JSON.stringify(!isOpen));
setOpen(!isOpen);
};
return (
<div className="App">
<header className="App-header">
<div>{!isOpen && <div>Content <button onClick={handleToggle}>Toggle</button></div>}</div>
</header>
</div>
);
}
export default App;
Edit: This is the full production file with Reza Zare's fix that now throws the error 'import' and 'export' may only appear at the top level on line 65:
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import BundleContainer from '../containers/bundle_container';
import ColumnLoading from './column_loading';
import DrawerLoading from './drawer_loading';
import BundleColumnError from './bundle_column_error';
import {
Compose,
Notifications,
HomeTimeline,
CommunityTimeline,
PublicTimeline,
HashtagTimeline,
DirectTimeline,
FavouritedStatuses,
BookmarkedStatuses,
ListTimeline,
Directory,
} from '../../ui/util/async-components';
import ComposePanel from './compose_panel';
import NavigationPanel from './navigation_panel';
import { supportsPassiveEvents } from 'detect-passive-events';
import { scrollRight } from '../../../scroll';
const componentMap = {
'COMPOSE': Compose,
'HOME': HomeTimeline,
'NOTIFICATIONS': Notifications,
'PUBLIC': PublicTimeline,
'REMOTE': PublicTimeline,
'COMMUNITY': CommunityTimeline,
'HASHTAG': HashtagTimeline,
'DIRECT': DirectTimeline,
'FAVOURITES': FavouritedStatuses,
'BOOKMARKS': BookmarkedStatuses,
'LIST': ListTimeline,
'DIRECTORY': Directory,
};
// Added const
const getInitialIsOpen = () => JSON.parse(localStorage.getItem('is-open')) || false;
const App = () => {
const [isOpen, setOpen] = React.useState(getInitialIsOpen());
const handleToggle = () => {
localStorage.setItem('is-open', JSON.stringify(!isOpen));
setOpen(!isOpen);
};
function getWeekNumber(d) {
d = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()));
d.setUTCDate(d.getUTCDate() + 4 - (d.getUTCDay()||7));
var yearStart = new Date(Date.UTC(d.getUTCFullYear(),0,1));
var weekNo = Math.ceil(( ( (d - yearStart) / 86400000) + 1)/7);
return [d.getUTCFullYear(), weekNo];
}
var result = getWeekNumber(new Date());
// errors out here: 'import' and 'export' may only appear at the top level.
export default class ColumnsArea extends ImmutablePureComponent {
static contextTypes = {
router: PropTypes.object.isRequired,
};
static propTypes = {
columns: ImmutablePropTypes.list.isRequired,
isModalOpen: PropTypes.bool.isRequired,
singleColumn: PropTypes.bool,
children: PropTypes.node,
};
// Corresponds to (max-width: $no-gap-breakpoint + 285px - 1px) in SCSS
mediaQuery = 'matchMedia' in window && window.matchMedia('(max-width: 1174px)');
state = {
renderComposePanel: !(this.mediaQuery && this.mediaQuery.matches),
}
componentDidMount() {
if (!this.props.singleColumn) {
this.node.addEventListener('wheel', this.handleWheel, supportsPassiveEvents ? { passive: true } : false);
}
if (this.mediaQuery) {
if (this.mediaQuery.addEventListener) {
this.mediaQuery.addEventListener('change', this.handleLayoutChange);
} else {
this.mediaQuery.addListener(this.handleLayoutChange);
}
this.setState({ renderComposePanel: !this.mediaQuery.matches });
}
this.isRtlLayout = document.getElementsByTagName('body')[0].classList.contains('rtl');
}
componentWillUpdate(nextProps) {
if (this.props.singleColumn !== nextProps.singleColumn && nextProps.singleColumn) {
this.node.removeEventListener('wheel', this.handleWheel);
}
}
componentDidUpdate(prevProps) {
if (this.props.singleColumn !== prevProps.singleColumn && !this.props.singleColumn) {
this.node.addEventListener('wheel', this.handleWheel, supportsPassiveEvents ? { passive: true } : false);
}
}
componentWillUnmount () {
if (!this.props.singleColumn) {
this.node.removeEventListener('wheel', this.handleWheel);
}
if (this.mediaQuery) {
if (this.mediaQuery.removeEventListener) {
this.mediaQuery.removeEventListener('change', this.handleLayoutChange);
} else {
this.mediaQuery.removeListener(this.handleLayouteChange);
}
}
}
handleChildrenContentChange() {
if (!this.props.singleColumn) {
const modifier = this.isRtlLayout ? -1 : 1;
this._interruptScrollAnimation = scrollRight(this.node, (this.node.scrollWidth - window.innerWidth) * modifier);
}
}
handleLayoutChange = (e) => {
this.setState({ renderComposePanel: !e.matches });
}
handleWheel = () => {
if (typeof this._interruptScrollAnimation !== 'function') {
return;
}
this._interruptScrollAnimation();
}
setRef = (node) => {
this.node = node;
}
renderLoading = columnId => () => {
return columnId === 'COMPOSE' ? <DrawerLoading /> : <ColumnLoading multiColumn />;
}
renderError = (props) => {
return <BundleColumnError multiColumn errorType='network' {...props} />;
}
render () {
const { columns, children, singleColumn, isModalOpen } = this.props;
const { renderComposePanel } = this.state;
if (singleColumn) {
return (
<div className='columns-area__panels'>
<div className='columns-area__panels__pane columns-area__panels__pane--compositional'>
<div className='columns-area__panels__pane__inner'>
{renderComposePanel && <ComposePanel />}
</div>
</div>
<div className='columns-area__panels__main'>
<div className='tabs-bar__wrapper'><div id='tabs-bar__portal' />
// output of getInitialIsOpen
<div class='banner'>
{!isOpen && <div>Content <button onClick={handleToggle}>Toggle</button></div>}
</div>
</div>
<div className='columns-area columns-area--mobile'>{children}</div>
</div>
<div className='columns-area__panels__pane columns-area__panels__pane--start columns-area__panels__pane--navigational'>
<div className='columns-area__panels__pane__inner'>
<NavigationPanel />
</div>
</div>
</div>
);
}
return (
<div className={`columns-area ${ isModalOpen ? 'unscrollable' : '' }`} ref={this.setRef}>
{columns.map(column => {
const params = column.get('params', null) === null ? null : column.get('params').toJS();
const other = params && params.other ? params.other : {};
return (
<BundleContainer key={column.get('uuid')} fetchComponent={componentMap[column.get('id')]} loading={this.renderLoading(column.get('id'))} error={this.renderError}>
{SpecificComponent => <SpecificComponent columnId={column.get('uuid')} params={params} multiColumn {...other} />}
</BundleContainer>
);
})}
{React.Children.map(children, child => React.cloneElement(child, { multiColumn: true }))}
</div>
);
}
}

React: why state or props null on page refresh?

I am displaying a tree view on left frame of the page. Tree is generating from xml file. On the click of each node, components are opening in the right frame of the page. ProductsTreeView is the tree component, Add_Category is the component that will open on the click of one of the tree node. I am passing the props through routing. everything is working fine as long as the page not refresh. In case of page refresh, props is showing null in the Add_Category page. Please help how to fix this.
[1]: https://i.stack.imgur.com/QeYB6.gif
App.js
import React, { Component } from 'react';
import { Switch, Route, BrowserRouter, Redirect } from "react-router-dom";
import Home from './components/Home';
export class App extends Component {
render() {
return (
<div>
<BrowserRouter>
<Switch>
<Route path="/" component={Home} />
<Redirect to="/" />
</Switch>
</BrowserRouter>
</div>
);
}
}
export default App;
_____________
Home.js
import React from 'react';
import { Route} from "react-router-dom";
import ProductsTree from '.ProductsTreeView';
import AddCategory from './Add_Category';
class Home extends React.Component
constructor(props) {
super(props);
this.state =
{
currentNode: {},
data: "",
};
this.setCurrentNode = this.setCurrentNode.bind(this);
}
setCurrentNode(node) {
this.setState({ currentNode: node });
}
render() {
return (
<div>
<table className="Container">
<tbody><tr width="100%">
<td className="TreeContainer">
<ProductsTree setCurrentNode={this.setCurrentNode} /> </td>
<td className="BodyContainer">
<Route path="/Add_Category">
<AddCategory key_id={this.state.currentNode.key_id} />
</Route>
</td> </tr> </tbody> </table>
</div>
);
}
}
export default Home;
_________________________
***Add_Category***
import React from 'react'
export class Add_Category extends React.Component {
constructor(props) {
super(props);
this.state = {
ID: "",
Name: "",
};
}
componentDidMount() {
if (typeof this.props.key_id !== 'undefined') {
const ID= this.props.key_id;
this.getName(ID);
}
}
componentDidUpdate(prevProps) {
if (prevProps.key_id !== this.props.key_id) {
console.log(`key_id: ${this.props.key_id}`);
const ID= this.props.key_id;
this.getName(ID);
}
}
async getName(ID) {
await fetch(REQUEST_URL)
.then(response => response.json())
.then((data) => {
this.setState({
Name: data,
ID: this.props.key_id,
loading: false})
console.log(this.state.Name)
})[![enter image description here][1]][1]
}
render() {
return (
<div>
<form>
{this.state.Name}
</form>
</div>
);
}
}
export default Add_Category;
_________________________
ProductsTreeView.js
import React, { Component } from 'react';
import ReactHtmlParser from 'react-html-parser';
import axios from 'axios';
import XMLParser from 'react-xml-parser';
import { Link } from "react-router-dom";
class ProductsTreeView extends Component {
render() {
return (
<div id="TreeView">
<TreeView setCurrentNode={this.props.setCurrentNode} />
</div>
);
}
}
class Node {
description = 'n/a';
id = -1;
key_id = -1;
linkpagename = '';
isActive = false;
nodes = [];
constructor(description, id, key_id, linkpagename) {
this.description = description;
this.id = id;
this.key_id = key_id;
this.linkpagename = linkpagename;
}
static nodesFromXml(xml) {
const map = (entity, nodes) => {
const id = entity.attributes['id'];
const key_id = entity.attributes['key-id'];
const descriptionText =
entity.children[
entity.children.findIndex((child) => child.name === 'description')
].value;
const entities = entity.children.filter(
(child) => child.name === 'entity'
);
var linkPageName = entity.attributes['link-page-name'];
linkPageName = linkPageName.replace(".aspx", "");
const node = new Node(descriptionText, id, key_id, linkPageName);
nodes.push(node);
entities.forEach((entity) => map(entity, node.nodes));
};
const parsedData = new XMLParser().parseFromString(xml);
const entities = parsedData.children.filter(
(child) => child.name === 'entity'
);
const nodes = [];
entities.forEach((entity) => map(entity, nodes));
return nodes;
}
}
class TreeView extends React.Component {
constructor(props) {
super(props);
this.state = { nodes: [] };
this.toggleNode = this.toggleNode.bind(this);
}
componentDidMount() {
axios
.get(REQUEST_URL, { 'Content-Type': 'application/xml; charset=utf-8' })
.then((response) =>
this.setState({ nodes: Node.nodesFromXml(response.data) }))
.catch(function (error) {
if (error.response) {
// Request made and server responded
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
} else if (error.request) {
// The request was made but no response was received
console.log(error.request);
} else {
// Something happened in setting up the request that triggered an Error
console.log('Error', error.message);
}
});
}
render() {
const nodes = this.state.nodes;
return (
<ul>
{nodes.map((node) => (
<TreeNode
id={node.id}
key={node.key_id}
node={node}
onToggle={this.toggleNode}
setCurrentNode={this.props.setCurrentNode}
/>
))}
</ul>
);
}
toggleNode(node) {
this.props.setCurrentNode(node);
function _toggleNode(currentNode, node) {
if (currentNode.id === node.id) { //currentNode.id === node.id)
{
if (currentNode.key_id === node.key_id)
{
currentNode.isActive = !currentNode.isActive;
}
}
}
else
{
currentNode.nodes.forEach((childNode) => _toggleNode(childNode, node));
}
}
const nodes = this.state.nodes;
nodes.forEach((currentNode) => _toggleNode(currentNode, node));
this.setState((state) => (state.nodes = nodes));
}
}
class TreeNode extends React.Component {
render() {
const node = this.props.node;
const onToggle = this.props.onToggle;
let activeChildren = null;
if (node.isActive && node.nodes.length > 0) {
activeChildren = (
<ul>
{node.nodes.map((node) => (
<TreeNode
id={node.id}
key={node.key_id}
node={node}
onToggle={onToggle}
/>
))}
</ul>
);
}
return (
<li
id={node.id} linkpagename={node.linkpagename}
key={node.key_id}
onClick={(event) => {
event.stopPropagation();
onToggle(node);
}}
>
<Link to={node.linkpagename} style={{ textDecoration: 'none', color: '#000000' }} >
{node.description}</Link>
{activeChildren}
</li>
);
}
}
export default ProductsTreeView;
thanks
React state is ephemeral, it lives in memory. When the page is reloaded the app is reloaded. Anything in state is reset.
This appears to be a case of needing to persist the React state to longer-term storage so when the page is reloaded the state can be reinitialized.
Here's an example:
const initialState = {
currentNode: {},
data: "",
};
class Home extends React.Component
constructor(props) {
super(props);
this.state = initialState;
this.setCurrentNode = this.setCurrentNode.bind(this);
}
componentDidMount() {
// initialize from storage
this.setState(JSON.parse(localStorage.getItem("homeState")) ?? initialState);
}
componentDidUpdate() {
// persist updates to storage
localStorage.setItem("homeState", JSON.stringify(this.state));
}
setCurrentNode(node) {
this.setState({ currentNode: node });
}
render() {
return (
<div>
<table className="Container">
<tbody>
<tr width="100%">
<td className="TreeContainer">
<ProductsTree setCurrentNode={this.setCurrentNode} />
</td>
<td className="BodyContainer">
<Route path="/Add_Category">
<AddCategory key_id={this.state.currentNode.key_id} />
</Route>
</td>
</tr>
</tbody>
</table>
</div>
);
}
}
If other components have state that needs to persist through page reloads they will also need to do this.
AddCategory
const initialState = {
ID: "",
Name: "",
}
const getStorageKey = (id) => `addCategory-${id}`;
export class AddCategory extends React.Component {
constructor(props) {
super(props);
this.state = initialState;
}
componentDidMount() {
const { key_id } = this.props;
// initialize from storage
this.setState(
JSON.parse(localStorage.getItem(getStorageKey(key_id))) ?? initialState
);
if (typeof key_id !== 'undefined') {
this.getName(key_id);
}
}
componentDidUpdate(prevProps, prevState) {
const { ID } = this.state;
const { key_id } = this.props;
if (prevProps.key_id !== key_id) {
console.log(`key_id: ${key_id}`);
this.getName(key_id);
}
if (prevState.ID !== ID) {
// persist updates to storage
localStorage.setItem(getStorageKey(ID), JSON.stringify(this.state));
}
}
async getName(ID) {
const response await fetch(REQUEST_URL);
const data = await response.json();
this.setState({
Name: data,
ID,
loading: false
});
}
render() {
...
}
}
TreeView
class TreeView extends React.Component {
constructor(props) {
super(props);
this.state = { nodes: [] };
this.toggleNode = this.toggleNode.bind(this);
}
componentDidMount() {
// initialize from storage
const storedState = JSON.parse(localStorage.getItem("treeview"));
if (storedState) {
this.setState(storedState);
} else {
axios.get(REQUEST_URL, { 'Content-Type': 'application/xml; charset=utf-8' })
.then((response) => {
this.setState(
{ nodes: Node.nodesFromXml(response.data) },
() => {
// persist updated state to storage
localStorage.setItem("treeview", JSON.stringify(this.state));
}
);
})
.catch(function (error) {
...
});
}
}
...
}

Does changing the props of a child always re-render the parent even with React.memo?

I'm trying to prevent a modal element to re-render when it's supposed to be invisible.
The course I'm following does this by converting the component to a class based component and using shouldComponentUpdate() but I wanted to check if using React.memo() did the same thing.
I tried, and it doesn't, but I'm not sure why.
The component that should not render is this:
import React , {useEffect} from 'react'
import Aux from '../../../hoc/Aux';
import Backdrop from '../Backdrop/Backdrop';
import classes from './Modal.module.css';
const Modal = (props) => {
useEffect(() => {
console.log('[Modal.js] useEffect')
});
return (
<Aux>
<Backdrop show={props.show} clicked={props.modalClosed} />
<div
className={classes.Modal}
style={{
transform: props.show ? 'translateY(0)' : 'translateY(-100vh)',
opacity: props.show ? '1': '0'
}}>
{props.children}
</div>
</Aux>)
};
export default React.memo(Modal);
Which is managed by
import React, {Component} from 'react'
import Aux from '../../hoc/Aux';
import Burger from '../../components/Burger/Burger'
import BuildControls from '../../components/Burger/BuildControls/BuildControls'
import Modal from '../../components/UI/Modal/Modal'
import OrderSummary from '../../components/Burger/OrderSummary/OrderSummary'
const INGREDIENT_PRICES = {
salad: 0.5,
cheese: 0.4,
meat: 1.3,
bacon: 0.7
}
class BurgerBuilder extends Component {
state = {
ingredients: {
salad: 0,
bacon: 0,
cheese: 0,
meat: 0
},
totalPrice: 4,
purchasable: false,
purchasing: false
}
purchaseHandler = () => {
this.setState({purchasing: true});
};
purchaseCancelHandler = () => {
this.setState({purchasing:false});
};
purchaseContinueHandler = () => {
alert('You Continue!')
};
updatePurchaseState = (ingredients) => {
let purchasable = false;
for (let ingredient in ingredients){
if (ingredients[ingredient]>0){
purchasable = true;
break;
}
}
this.setState({purchasable:purchasable})
}
addIngredientHandler = (type) => {
const oldCount = this.state.ingredients[type];
const updatedCount = oldCount +1;
const updatedIngredients = {
...this.state.ingredients
};
updatedIngredients[type] = updatedCount;
const priceAddition = INGREDIENT_PRICES[type];
const oldPrice = this.state.totalPrice;
const newPrice = oldPrice + priceAddition;
this.setState({totalPrice: newPrice, ingredients: updatedIngredients});
this.updatePurchaseState(updatedIngredients);
};
removeIngredientHandler = (type) => {
const oldCount = this.state.ingredients[type];
if (oldCount <= 0)
return;
const updatedCount =oldCount -1;
const updatedIngredients = {
...this.state.ingredients
};
updatedIngredients[type] = updatedCount;
const priceAddition =INGREDIENT_PRICES[type];
const oldPrice = this.state.totalPrice;
const newPrice = oldPrice - priceAddition;
this.setState({totalPrice: newPrice, ingredients: updatedIngredients});
this.updatePurchaseState(updatedIngredients);
};
render () {
const disabledInfo = {
...this.state.ingredients
};
for (let key in disabledInfo){
disabledInfo[key] = disabledInfo[key] <= 0;
}
return (
<Aux>
<Modal show={this.state.purchasing} modalClosed={this.purchaseCancelHandler}>
<OrderSummary
ingredients={this.state.ingredients}
purchaseCancelled={this.purchaseCancelHandler}
purchaseContinued={this.purchaseContinueHandler}
price={this.state.totalPrice} />
</Modal>
<Burger ingredients={this.state.ingredients}/>
<BuildControls
ingredientAdded={this.addIngredientHandler}
ingredientRemoved={this.removeIngredientHandler}
disabled={disabledInfo}
price={this.state.totalPrice}
purchasable={this.state.purchasable}
ordered={this.purchaseHandler}/>
</Aux>
);
}
}
export default BurgerBuilder;
With BuildControls I change the Ingredients state; but not the props I pass to modal, purchasing and purchaseHandler
Does changing the ingredients prop I pass to it's child always prompt a re-render even when Modal itself is under React.memo() ?
You are changing one of the props you pass to Modal - the children prop. It is passed implicitly by adding children to a react element. And since you are changing the child element it will cause a re-render.

How do I convert Class Components to Functional Components in React.js project?

In the project I watch, they work with class component, but I want to do these operations with functional component using hooks. How can you help me guys? I tried many times but couldn't do this translation. I'm still trying
My code (imported data is "ingredients"):
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
ingredients: [],
totalPrice: 0
}
this.addIngredients = this.addIngredients.bind(this)
this.removeIngredients = this.removeIngredients.bind(this)
this.calculateTotal = this.calculateTotal.bind(this)
}
addIngredients(product) {
this.setState({
ingredients: [...this.state.ingredients].concat([
{ ...product, displayId: Math.random() }
])
})
}
removeIngredients(product) {
const selectedProduct = this.state.ingredients.find((ingredient) => {
return ingredient.name === product.name
})
const targetId = selectedProduct.displayId
this.setState({
ingredients: this.state.ingredients.filter((ingredient) => {
return ingredient.displayId !== targetId
})
})
}
calculateTotal() {
let total = 4
this.state.ingredients.forEach((item) => {
total += item.price
})
return total.toFixed(2)
}
render() {
return (
<div>
<Hamburger ingredients={this.state.ingredients} />
<TotalPrice total={this.calculateTotal} />
<ItemList
items={ingrediends}
addIngredients={this.addIngredients}
removeIngredients={this.removeIngredients}
selectedIngredients={this.state.ingredients}
/>
</div>
)
}
}
export default App
Navarrro I hope this helps you! I couldn't test it but Is a good started for you, I use ES6 syntax...
import React, { useState } from 'react';
import { Hamburger, TotalPrice, ItemList } from './SuperComponents.jsx';
const App = () => {
const [ingredients, setIngredients] = useState([]);
// You are not using this state
// const [totalPrice, setTotalPrice] = useState(0);
const addIngredients = (product) => {
setIngredients([...ingredients, { ...product, displayId: Math.random() }]);
};
const removeIngredients = (product) => {
const selectedProduct = ingredients.find(
(ingredient) => ingredient.name === product.name
);
const { targetId } = selectedProduct;
setIngredients(
ingredients.filter((ingredient) => ingredient.displayId !== targetId)
);
};
const calculateTotal = () => {
let total = 4;
ingredients.forEach((item) => (total += item.price));
return total.toFixed(2);
};
return (
<>
<Hamburger ingredients={ingredients} />
<TotalPrice total={() => calculateTotal()} />
<ItemList
items={ingredients}
addIngredients={(i) => addIngredients(i)}
removeIngredients={(i) => removeIngredients(i)}
selectedIngredients={ingredients}
/>
</>
);
};
export default App;

React route doesn't render component even if component is rendering

Disclaimer: I've seen Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null but no solution from this question fixes my problem
I am trying to render a component inside a React route like this in App.js
<main style={{marginTop: '1px'}}>
<div className="App">
<Switch>
<Route exact path ='/' render = {() => <Home toggleFilter={this.state.filterDrawerOpen}/>}/>
<Route path='/profile' component ={Profile}/>
</Switch>
</div>
</main>
But the Home component never get's rendered even tho I can see console logs from the Home component render in the console like in this picture console logs
The home component is 400+ lines long so I'll include just the relevant code
import React, { Component } from 'react';
import Auth from '#aws-amplify/auth';
import { API } from 'aws-amplify';
import ProfileRedirect from "./components/ProfileRedirect";
import LoadingAnimation from './components/LoadingAnimation';
import ReadingSpeed from "./components/ReadingSpeed";
import './Tags.css';
import Articles from "./components/Articles";
import { CSSTransitionGroup } from 'react-transition-group'
import FilterArea from './components/FilterArea';
import "react-datepicker/dist/react-datepicker.css";
import FilterDrop from './components/FilterDrop';
import FilterDrawer from './components/FilterDrawer';
import { withRouter } from "react-router";
let apiName = 'userApi';
let path = '/users/';
class Home extends Component {
constructor(props){
super(props);
this.state = {
isLoading: true,
firstLogIn: true,
filterDrawerOpen:false,
user :{
phone:"",
readingSpeed:0,
email:"",
username:"",
articles: [],
interests: [],
saved:[],
filters:{
minutes:null,
authors:[],
sources:[],
minDate:{},
selectedInterests:[],
selectedDate:{}
}
}
}
this.dateFilter = this.dateFilter.bind(this)
}
async componentDidMount(){
let userEntry;
let date = new Date();
date.setMonth(date.getMonth()-1)
// const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
const loggedUser = await Auth.currentAuthenticatedUser();
userEntry = await API.get(apiName,path + loggedUser.username);
if(userEntry.hasOwnProperty("userName")){
let uniqueResults;
let results = await this.callDatabase(userEntry)
uniqueResults = results.reduce(function (p, c) {
if (!p.some(function (el) {return (el.title === c.title && el.author === c.author);}))
p.push(c);
return p;
}, []);
this.setState({
isLoading:false,
firstLogIn:false,
filterDrawerOpen:false,
user : {
phone:userEntry.userName,
readingSpeed:userEntry.readingSpeed,
email:userEntry.userEmail,
username:userEntry.userName,
articles: uniqueResults,
interests:userEntry.userInterests,
saved: userEntry.savedArticles,
filters:{
minutes:null,
authors:[],
sources:[],
minDate:date,
selectedDate:{},
selectedInterests:[]
}
}
})
}else {
this.setState({
isLoading:false
})
}
}
async callDatabase (userEntry,sources,freeMode){...}
authorFilter = selected => {...}
sourceFilter = selected => {...}
interestFilter = selected => {...}
minutesFilter(value) {...}
componentWillReceiveProps(newProps) {
if(newProps.toggleFilter !== this.props.toggleFilter){
this.filterToggleClickHandler();
}
}
filterToggleClickHandler = () => {...}
filterDropClickHandler = () => {...}
dateFilter(selected) {...}
generateOptions = filter => {
let data;
if (filter === "author"){
data = this.state.user.articles.reduce(function (p, c) {
if (!p.some(function (el) { return (el.author === c.author); }))
p.push(c);
return p;
}, [])
}else if (filter==="source"){
let tempData;
tempData = this.state.user.articles.reduce(function (p, c) {
if (!p.some(function (el) { return (el.source.name === c.source.name); }))
p.push(c);
return p;
}, [])
data = tempData.map(element => element.source)
}else if (filter==="interest"){
data = this.state.user.articles.reduce(function (p, c) {
if (!p.some(function (el) { return (el.interest === c.interest); }))
p.push(c);
return p;
}, [])
}
return data
}
async updateDataBase(readingSpeed){
let updates = {
body:{
userName:this.state.user.username,
userEmail:this.state.user.email,
userPhone:this.state.user.phone,
userInterests:this.state.user.interests,
savedArticles:this.state.user.saved,
readingSpeed:readingSpeed,
}
}
return await API.put(apiName,path,updates);
}
filtersArea() {
let check
let newDate = this.state.user.filters.selectedDate;
if(newDate === null){
check = true
}else {
check= Object.entries(newDate).length === 0 && newDate.constructor === Object
}
return (
<div className="container-fluid">
<div className="col" style={{margin:"0",padding:"6"}}>
<FilterArea
sourceOptions = {this.generateOptions("source")}
interestOptions = {this.generateOptions("interest")}
authorOptions = {this.generateOptions("author")}
sourceFilter = {this.sourceFilter.bind(this)}
interestFilter = {this.interestFilter.bind(this)}
authorFilter = {this.authorFilter.bind(this)}
selected={!check ? this.state.user.filters.selectedDate:undefined}
minDate = {this.state.user.filters.minDate}
dateFilter = {this.dateFilter.bind(this)}
minutesFilter = {this.minutesFilter.bind(this)}
/>
<FilterDrawer
show = {this.state.filterDrawerOpen}
sourceOptions = {this.generateOptions("source")}
interestOptions = {this.generateOptions("interest")}
authorOptions = {this.generateOptions("author")}
sourceFilter = {this.sourceFilter.bind(this)}
interestFilter = {this.interestFilter.bind(this)}
authorFilter = {this.authorFilter.bind(this)}
selected={!check ? this.state.user.filters.selectedDate:undefined}
minDate = {this.state.user.filters.minDate}
dateFilter = {this.dateFilter.bind(this)}
minutesFilter = {this.minutesFilter.bind(this)}
/>
</div>
</div>
);
}
checkAuthors(filter,data){
let result = [];
let articles = data.map(function(article){
if(filter.includes(article.author))result.push(article);
})
return result
}
checkSource(filter,data){
let result = [];
let articles = data.map(function(article) {
if(filter.includes(article.source.name)) result.push(article)
})
return result
}
checkInterest(filter,data){
let result = [];
let articles = data.map(function(article){
if(filter.includes(article.interest))result.push(article);
})
return result
}
checkMinutes(filter,filter1,data){
let result = [];
let articles = data.map(function (article) {
if(article.hasOwnProperty("charNumber")){
if((article.charNumber/filter1)<=filter)result.push(article)
}
})
return result
}
checkDate(filter,data){
let result = [];
let dA;
let dB = filter;
let articles = data.map(function(article){
dA = new Date(article.publishedAt.substring(0,10))
if(dB<=dA) result.push(article)
})
return result
}
render() {
let filterdrop;
if(this.state.filterDrawerOpen) {
filterdrop = <FilterDrop click = {this.filterDropClickHandler}/>
}
console.log(this.state)
const stillLoading = () => {
return (
<div className="loading">
<LoadingAnimation
type = {"spinningBubbles"}
color = {"aqua"}
/>
</div>);
}
const articles = (filterA, filterS, filterI, filterM) => {
let articles = this.state.user.articles;
let newDate = this.state.user.filters.selectedDate;
let readingTime = this.state.user.readingSpeed;
let check;
if(newDate === null){
check = true
}else {
check= Object.entries(newDate).length === 0 && newDate.constructor === Object
}
if(!check){
articles = this.checkDate(newDate,articles)
}
if(filterA.length){
articles = this.checkAuthors(filterA,articles)
}
if(filterS.length){
articles = this.checkSource(filterS,articles)
}
if(filterI.length){
articles = this.checkInterest(filterI,articles)
}
if(!(filterM === null) && filterM!==0){
articles = this.checkMinutes(filterM,readingTime,articles)
}
return(
<div className="wrapper">
{
articles.map(function(article) {
return (
<Articles
article = {article}
key = {article.id}
/>
)
})}
</div> );
}
if(this.state.isLoading){
return (
stillLoading()
);
}else if(!this.state.firstLogIn && this.state.user.articles.length>0 && this.state.user.readingSpeed >0){
return (
<CSSTransitionGroup
transitionName="example"
transitionAppear={true}
transitionAppearTimeout={1000}
transitionEnter={false}
transitionLeave={false}>
{this.filtersArea()}
{filterdrop}
{articles(this.state.user.filters.authors,this.state.user.filters.sources,
this.state.user.filters.selectedInterests,this.state.user.filters.minutes)}
</CSSTransitionGroup>
);
}else if(this.state.firstLogIn || this.state.user.readingSpeed === 0){
return(
<ReadingSpeed updateDb = {this.updateDataBase.bind(this)}/>
);
}
else if(this.state.user.interests.length === 0){
return(
<div>
<ProfileRedirect/>
</div>
);
}
}
}
export default Home;
I've tried things like
const MyHome =(props) => {
return (
<Home {...props} toggleFilter = {this.state.filterDrawerOpen}/>
)
}
Instead of rendering directly in the Route but nothing seems to work.
Here is the code for index.js as well
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import 'bootstrap/dist/css/bootstrap.css';
import {BrowserRouter as Router, Route} from 'react-router-dom';
ReactDOM.render(
<Router>
<Route path='/' render={(props )=> <App {...props}/>}/>
</Router>,
document.getElementById('root'));
serviceWorker.unregister();
What am I missing?

Categories

Resources