I am getting issues while rendering contact data.
Here the case is when I click the continue button in my app it triggers the checkoutContinuedHandler() function that results in a change of URL but the ContactData component is not rendered and my CheckoutSummary component also vanishes as I am rendering it on the same page.
I Checked twice that export is done and there is no spelling mistakes.
I tried different solutions from the stack and discussed them with my mate still the issue is on...
import React, { Component } from "react";
import { Route } from "react-router-dom";
import CheckoutSummary from "../../components/Order/CheckoutSummary/CheckoutSummary";
import ContactData from "./ContactData/ContactData";
class Checkout extends Component {
state = {
ingredients: {
salad: 1,
meat: 1,
cheese: 1,
bacon: 1,
},
};
componentDidMount() {
const query = new URLSearchParams(this.props.location.search);
const ingredients = {};
for (let param of query.entries()) {
// ['salad','1']
ingredients[param[0]] = +param[1];
}
this.setState({ ingredients: ingredients });
}
checkoutCancelledHandler = () => {
this.props.history.goBack();
};
checkoutContinuedHandler = () => {
this.props.history.replace("/checkout/contact-data");
console.log(this);
};
render() {
return (
<div>
<CheckoutSummary
ingredients={this.state.ingredients}
checkoutCancelled={this.checkoutCancelledHandler}
checkoutContinued={this.checkoutContinuedHandler}
/>
<Route
path={this.props.match.path + "/contact-data"}
component={ContactData}
/>
</div>
);
}
}
export default Checkout;
Related
I am super stuck with ReactJs in trying to add one Virtual Component to another Component at runtime and failing to do so. Here is what I am trying to do:
My App.js looks like this:
import React from 'react';
import './App.css';
// Components
import Header from './components/Header';
import Footer from './components/Footer';
import LeftSideSpace from './components/LeftSideSpace';
import RightSideSpace from './components/RightSideSpace';
import CenterSpace from './components/CenterSpace';
// main class name: App
class App extends React.Component {
// main function name: render
render() {
return (
<div className="App">
<Header title='My Blog'/>
<LeftSideSpace/>
<CenterSpace/>
<RightSideSpace/>
<Footer title='Welcome! This is my Blog site'/>
</div>
);
}
}
export default App;
My focus is on the component <CenterSpace/> which I am importing from here:
import React from 'react';
import PropTypes from 'prop-types'
class CenterSpace extends React.Component {
render() {
return (
<centerspace className="Site.CenterSpace">
<div id="Site.CenterSpace.Content">
{this.props.children}
</div>
</centerspace>
);
}
}
// props defaults
CenterSpace.defaultProps = {
title: 'Personal Blogger\'s site'
}
// props validations
CenterSpace.propTypes = {
title: PropTypes.string.isRequired
}
export default CenterSpace
Then I have a menu component like this, as of now, this is what I have in code, which I am sure contains bugs:
import React from 'react';
import PropTypes from 'prop-types'
import CenterSpace from '../CenterSpace'
import HomeLists from './HomeLists'
class MainMenu extends React.Component {
render() {
return (
<div className="Site.MainMenu">
<button onClick={this.props.onClickHome}>Home</button>
<button onClick={this.props.onClickBlogs}>Blogs</button>
<button onClick={this.props.onClickAboutMe}>About Me</button>
</div>
);
}
}
// props defaults
MainMenu.defaultProps = {
//control button clicks
onClickHome: () => {
var home_dom = new HomeLists();
var center_dom = new CenterSpace<String>("My Blog list");
console.log("say we went to home")
center_dom.appendChild(home_dom);
},
onClickBlogs:() => {
console.log("say we went to blogs")
},
onClickAboutMe:() => {
console.log("say we went to about me")
}
}
// props validations
MainMenu.propTypes = {
onClickHome: PropTypes.func.isRequired,
onClickBlogs: PropTypes.func.isRequired,
onClickAboutMe: PropTypes.func.isRequired,
}
export default MainMenu
This main-menu is used to dynamically add and remove components, but I am failing to do so. When I click Home button, the action I am trying achieveis to add <HomeList/> component to <CenterSpace/>. And futher, <HomeList/> is parsing some Json files and appending as child divs.
<HomeList/> looks like this (may have some issues, was not able to make it work, but that is something I can fix):
import React from 'react';
import PropTypes from 'prop-types'
class HomeLists extends React.Component {
render() {
const fs_obj = require('fs');
const fs_path = require('path');
const fs_jsons = fs_obj.readdirSync('../data').filter(file => fs_path.extname(file) === '.json');
fs_jsons.forEach(file => {
const file_data = fs_obj.readFileSync(fs_path.join('../data', file));
const json = JSON.parse(file_data.toString());
const blog_title = json.title;
var snippet_header = document.createElement('h3');
snippet_header.textContent(blog_title);
const blog_desp = json.blog.content[0].value;
var snippet_text = document.createElement('p');
snippet_text.textContent(blog_desp);
var snippet = document.createElement('div');
snippet.appendChild(snippet_header);
snippet.appendChild(snippet_text);
this.appendChild(snippet);
});
return (
<homelists className="Site.HomeLists">
<div id="Site.HomeLists.Content">{HomeLists}</div>
</homelists>
);
}
}
// props defaults
HomeLists.defaultProps = {
title: 'Personal Blogger\'s site'
}
// props validations
HomeLists.propTypes = {
title: PropTypes.string.isRequired
}
export default HomeLists
Right now when I click Home, all I get is the following error:
TypeError: center_dom.appendChild is not a function
onClickHome
src/components/complications/MainMenu.js:29
28 | console.log("say we went to home")
> 29 | center_dom.appendChild(home_dom);
| ^
30 | },
31 | onClickBlogs:() => {
32 |
console.log("say we went to blogs")
Can anyone help me get unblock from here.
Use the following component as an example for conditional rendering and it is based on your question as well.
import React from "react";
class MainMenu extends React.Component {
constructor(props) {
super(props);
this.state = { isHome: false, isBlogs: false, isAboutMe: false };
// Binding this keyword
this.onClickHome = this.onClickHome.bind(this);
this.onClickBlogs = this.onClickBlogs.bind(this);
this.onClickAboutMe = this.onClickAboutMe.bind(this);
}
onClickHome() {
this.setState({ isHome: true, isBlogs: false, isAboutMe: false });
}
onClickBlogs() {
this.setState({ isHome: false, isBlogs: true, isAboutMe: false });
}
onClickAboutMe() {
this.setState({ isHome: false, isBlogs: false, isAboutMe: true });
}
render() {
return (
<div className="Site.MainMenu">
<button onClick={this.onClickHome}>Home</button>
<button onClick={this.onClickBlogs}>Blogs</button>
<button onClick={this.onClickAboutMe}>About Me</button>
{this.state.isHome && <div>Home view is enabled</div>}
{this.state.isBlogs && <div>Blogs view is enabled</div>}
{this.state.isAboutMe && <div>AboutMe view is enabled</div>}
</div>
);
}
}
export default MainMenu;
Application View
Refer this link for more info on conditional rendering: https://reactjs.org/docs/conditional-rendering.html
I'm kind of new to react, so what i wanted was that, I have a toggle button to toggle a persons component and I have a cockpit component. But whenever I toggle the persons component, I don't want to always re-render the cockpit component.
So this is my Cockpit.js component file.
import React, { useEffect } from 'react';
import classes from './Cockpit.css';
const cockpit = props => {
useEffect(() => {
console.log('[Cockpit.js] useEffect');
// Http request...
setTimeout(() => {
alert('Saved data to cloud!');
}, 1000);
return () => {
console.log('[Cockpit.js] cleanup work in useEffect');
};
}, []);
useEffect(() => {
console.log('[Cockpit.js] 2nd useEffect');
return () => {
console.log('[Cockpit.js] cleanup work in 2nd useEffect');
};
});
// useEffect();
const assignedClasses = [];
let btnClass = '';
if (props.showPersons) {
btnClass = classes.Red;
}
if (props.personsLength <= 2) {
assignedClasses.push(classes.red); // classes = ['red']
}
if (props.personsLength <= 1) {
assignedClasses.push(classes.bold); // classes = ['red', 'bold']
}
return (
<div className={classes.Cockpit}>
<h1>{props.title}</h1>
<p className={assignedClasses.join(' ')}>This is really working!</p>
<button className={btnClass} onClick={props.clicked}>
Toggle Persons
</button>
</div>
);
};
export default React.memo(cockpit);
And this is my App.js
import React, { Component } from 'react';
import Persons from '../Components/Persons/Persons';
import classes from './App.css';
import Cockpit from '../Components/Cockpit/Cockpit'
class App extends Component {
constructor(props) {
super(props);
console.log("[App.js] constructor");
}
state = {
persons: [{id: "abc", name: "", age: 45},
{id: "azz", name: "", age: 56},
{id: "asq", name: "", age: 62}],
showPersons: false,
showCockpit: true
}
static getDerivedStateFromProps(props, state) {
console.log("[App.js] getDerivedStateFromProps", props)
return state;
}
componentDidMount() {
console.log('[App.js] componentDidMount')
}
shouldComponentUpdate(nextProps, nextState) {
console.log('[App.js] shouldCompoentUpdate');
return true;
}
componentDidUpdate() {
console.log('[App.js] componentDidUpdate')
}
deletePersonHandler = (i) => {
const persons = [...this.state.persons];
persons.splice(i, 1);
this.setState({persons: persons})
}
switchNameHandler = (newName) => {
this.setState({persons: [{name: newName, age: 50}, {name: "Aysha", age: 56}, {name: "Momma", age: 62}]})
}
nameSwitchHandler = (event, id) => {
const personIndex = this.state.persons.findIndex(p => {
return p.id === id;
})
const person = {...this.state.persons[personIndex]}
person.name = event.target.value;
const persons = [...this.state.persons]
persons[personIndex] = person;
this.setState({persons: persons})
}
togglePersonHandler = () => {
let doesChange = this.state.showPersons;
this.setState({showPersons: !doesChange})
}
render() {
console.log("[App.js] render");
let person = null;
if(this.state.showPersons) {
person = (<Persons
persons={this.state.persons}
clicked={this.deletePersonHandler}
changed={this.nameSwitchHandler} />
);
}
return (
<div className={classes.App}>
<button onClick={() => this.setState({showCockpit: false})}>Remove Cockpit</button>
{this.state.showCockpit ? (<Cockpit
title={this.props.appTitle}
showPersons={this.state.showPersons}
personsLength={this.state.persons.length}
clicked={this.togglePersonHandler} />) : null}
{person}
</div>
);
}
}
export default App;
But even when I toggle it, useEffect in cockpit component still console logs in the browser console when its not supposed to. I can't seem to find what I am doing wrong.
As you can see in this image the useEffect component in cockpit still renders in the console......
Browser Console
React.memo will do a shallow equal comparison on the props object by default. That means it will check every top level item in the props for equality and if any of them changed it will re-render.
When you click your persons toggle button it will change showPersons in your App component wich is also a prop that you pass to <Cockpit>. Therefore it will re-render even with React.memo. If it wouldn't re-render it wouldn't correctly update your Button class adding or removing classes.Red because this is dependent on the showPersons prop.
It has nothing to do with your useEffect inside of cockpit which will only get called after it re-renders but doesn't cause it to re-render in the first place.
On the click of Toggle Persons, you are changing the state in App Component.
This results in the re-rendering of the App and Cockpit components.
useEffect(() => {
console.log('[Cockpit.js] 2nd useEffect');
return () => {
console.log('[Cockpit.js] cleanup work in 2nd useEffect');
};
});
The above code will trigger every render as you haven't provided dependency.
To fix this, you need to add a dependency to the above code.
Since showPersons change it detects it as changed props.
You can add an equality function in React.memo that tells react when to consider the memoization stale:
// Will only rerender when someValue changes
export default React.memo(Cockpit, (oldProps, newProps) => oldProps.someValue === newProps.someValue)
I'm trying to update my home componentstate by getting data from the redux store every time the store is updated. I'm not sure what's wrong with the code below. I can't listen to store changes in my `home component.
my dispatch function is handled in this class.
export class GanttFilter extends Component {
...
handleSubmit = () => {
this.gFilter.filterGanttData(this.formValues)
.then((result) => {
if (result.data)
this.props.dispatch(ganttActions.loadGanttData(result.data));
});
}
...
GanttFilter.propTypes = {
dispatch: PropTypes.func.IsRequired
};
function mapStateToProps(state) {
return {
ganttData: state.gantt.ganttData
};
}
export default connect(mapStateToProps)(GanttFilter);
What I would like to do every time dispatch is called and the data changes, is update the state in my home component. Here is the component.
export class Home extends Component {
constructor() {
super();
this.state = {
data: [],
links: []
};
}
render() {
return (
<div className="fill">
<Gantt data={this.state.data} links={this.state.links} />
</div>
);
}
}
Home.propTypes = {
data: PropTypes.IsRequired
};
function mapStateToProps(state) {
return {
data: state.gantt.ganttData
};
}
export default connect(mapStateToProps)(Home);
the function mapStateToProps is never hit when I set a break point. How can I listen to changes to the store from the home component and update state?
Edit: Here is the wrapper component
function renderApp() {
// This code starts up the React app when it runs in a browser. It sets up the routing
// configuration and injects the app into a DOM element.
const baseUrl = document.getElementsByTagName("base")[0].getAttribute("href");
ReactDOM.render(
<ReduxProvider store={store}>
<AppContainer>
<BrowserRouter children={routes} basename={baseUrl} />
</AppContainer>
</ReduxProvider>,
document.getElementById("react-app")
);
}
reducers
const actionTypes = require('../actions/actionTypes');
const gantt = {
ganttData: [],
selectedTask: null
};
export default function ganttReducer(state = gantt, action) {
switch (action.type) {
case actionTypes.loadGanttData:
return { ...state, ganttData: [...action.ganttData] };
default:
return state;
}
}
root reducer
import { combineReducers } from 'redux';
import gantt from './ganttReducer';
const rootReducer = combineReducers({
gantt
});
export default rootReducer;
actions
const actionTypes = require('./actionTypes');
export function loadGanttData(ganttData) {
return { type: actionTypes.loadGanttData, ganttData };
}
export function getSelectedTask(ganttTask) {
return { type: actionTypes.setSelectedTask, ganttTask };
}
Error:
Make sure you import your Home component using import Home from '...' as opposed to import { Home } from '...', otherwise you'd be grabbing the unconnected component. In general, I would also avoid exporting the unconnected component.
Change this:
render() {
return (
<div className="fill">
<Gantt data={this.state.data} links={this.state.links} />
</div>
);
}
To
render() {
return (
<div className="fill">
<Gantt data={this.props.data} links={this.state.links} />
</div>
);
}
Your data is comming from your props (redux), not from your state.
This is a reoccurring problem for me… Trying to figure out why an update to a single item in a component results in the entire component re-rendering. If I have a CSS fade in transition on the component, it fades in again when changing a child of the component.
I have a list of items, each with a link. Clicking the link adds the item to the cart. I have it set up to put that item in a “loading” state until the cart action is successful.
This used to work perfectly, but now it just re-renders the entire page, making it disappear for a second then reappear. I’m not entirely sure why.
This is the code stripped down to its basic bits:
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import autobind from 'class-autobind';
import Responsive from 'components/Responsive';
// Selectors
import { createStructuredSelector } from 'reselect';
import { selectCartLoading, selectCartMap, selectFavorites } from 'containers/App/selectors';
import { selectPackages } from 'store/fonts/selectors';
// Actions
import { addToCart } from 'containers/App/actions';
export class Packages extends React.Component {
constructor() {
super();
autobind(this);
}
state = {
loadingID: 0
}
componentWillReceiveProps(nextProps) {
if (this.props.cartLoading === true && nextProps.cartLoading === false) {
this.setState({ loadingID: 0 });
}
}
onAddToCart(e) {
e.preventDefault();
const { onAddToCart } = this.props;
const id = e.currentTarget.dataset.package;
const packageData = {
type: 'package',
id,
quantity: 1
};
onAddToCart(packageData);
this.setState({ loadingID: id });
}
render() {
const { cartMapping, packages } = this.props;
if (!packages) { return null; }
return (
<Responsive>
<div>
<ul>
{ packages.map((pack) => {
const inCart = !!cartMapping[parseInt(pack.id, 10)];
const isFavorited = !favorites ? false : !!find(favorites.favorites, (favorite) => parseInt(pack.id, 10) === favorite.items.id);
return (
<li key={ pack.id }>
<Icon iconName="heart" onClick={ (e) => this.onAddFavorite(e, pack) } />
<span>{ pack.name }</span>
{ inCart && <span>In Cart</span> }
{ !inCart && <a data-package={ pack.id } href="/" onClick={ this.onAddToCart }>Add to Cart</a> }
</li>
);
})}
</ul>
</div>
</Responsive>
);
}
}
Packages.propTypes = {
cartLoading: PropTypes.bool,
cartMapping: PropTypes.object,
onAddToCart: PropTypes.func.isRequired,
packages: PropTypes.array
};
Packages.defaultProps = {
cartLoading: null,
cartMapping: null,
packages: null
};
const mapStateToProps = createStructuredSelector({
cartLoading: selectCartLoading(),
cartMapping: selectCartMap(),
packages: selectPackages()
});
function mapDispatchToProps(dispatch) {
return {
onAddToCart: (data) => dispatch(addToCart(data)),
dispatch
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Packages);
So why does clicking on <a data-package={ pack.id } href="/" onClick={ this.onAddToCart }>Add to Cart</a> result in a complete component re-render?
In your onAddToCart function you are setting the state of the component which will by default trigger a re-render of the component. If you need to set the state but not cause a re-render you can add a shouldComponentUpdate() function and check the changes before issuing a re-render to the component.
Find out more about shouldComponentUpdate() and the rest of the component lifecycle here
Suppose I have the following Tree:
<LandingPage>
<PageHeader>
<Menu>
<ShoppingCart>
</Menu>
</PageHeader>
<MainPage>
<ShoppingCart>
</MainPage>
</LandingPage>
The component we care about is the ShoppingCart.
Upon mounting it (componentDidMount) ShoppingCart triggers an action, so that the ShoppingCartStore makes a request to a server and returns a list of articles - triggering a rerender of ShoppingCart .
The way it is set up now, there will always be two requests and two rerenders, because both components are in the dom.
One solution would be to have a common root trigger these requests, but that would be the LandingPage - and one would have to pass the data through PageHeader and Menu and MainPage.
Is there a better solution? Is that good enough?
I use an api.store for all data requests. I call the api.store in the entry app.js. Then I use an action that the api.store listens to for the initial data requests.
app.js
'use strict';
import React from 'react';
import ReactDom from 'react-dom';
import AppCtrl from './components/app.ctrl.js';
import Actions from './actions/api.Actions';
import ApiStore from './stores/Api.Store';
window.ReactDom = ReactDom;
Actions.apiInit();
ReactDom.render( <AppCtrl />, document.getElementById('react') );
api.store
import Reflux from 'reflux';
import Actions from '../actions/api.Actions';
import ApiFct from '../utils/sa.api';
let ApiStoreObject = {
newData: {
"React version": "0.14",
"Project": "ReFluxSuperAgent",
"currentDateTime": new Date().toLocaleString()
},
listenables: Actions,
apiInit() { ApiFct.setData(this.newData); },
apiInitDone() { ApiFct.getData(); },
apiSetData(data) { ApiFct.setData(data); }
}
const ApiStore = Reflux.createStore(ApiStoreObject);
export default ApiStore;
In the component that connects to a store the initial state calls the store data so if the data is already there you get it.
import React from 'react';
import BasicStore from '../stores/Basic.Store';
let AppCtrlSty = {
height: '100%',
padding: '0 10px 0 0'
}
const getState = () => {
return {
Data1: BasicStore.getData1(),
Data2: BasicStore.getData2(),
Data3: BasicStore.getData3()
};
};
class AppCtrlRender extends React.Component {
render() {
let data1 = JSON.stringify(this.state.Data1, null, 2);
let data2 = JSON.stringify(this.state.Data2, null, 2);
let data3 = JSON.stringify(this.state.Data3, null, 2);
return (
<div id='AppCtrlSty' style={AppCtrlSty}>
React 0.14 ReFlux with SuperAgent<br/><br/>
Data1: {data1}<br/><br/>
Data2: {data2}<br/><br/>
Data3: {data3}<br/><br/>
</div>
);
}
}
export default class AppCtrl extends AppCtrlRender {
constructor() {
super();
this.state = getState();
}
componentDidMount = () => { this.unsubscribe = BasicStore.listen(this.storeDidChange); };
componentWillUnmount = () => { this.unsubscribe(); };
storeDidChange = (id) => {
switch (id) {
case 'data1': this.setState({Data1: BasicStore.getData1()}); break;
case 'data2': this.setState({Data2: BasicStore.getData2()}); break;
case 'data3': this.setState({Data3: BasicStore.getData3()}); break;
default: this.setState(getState());
}
};
}
From https://github.com/calitek/ReactPatterns React.14/ReFluxSuperAgent.