I'm using rc-slider to take a bunch of values from an array and render them in my component.
const array = ["Hey", "Ho", "Me", "Too", "See"]
class App extends Component {
state = { sliderValues: [0, 1, 2, 3, 4] };
handleChange = sliderValues => {
this.setState({ sliderValues }, () => console.log(array[this.state.sliderValues]));
};
render() {
const renderContent = array.map((value, index) => {
console.log(value, 'value')
console.log(index, 'index')
if (index === this.state.sliderValues) {
return (
<div>
<h1>{value}</h1>
</div>
)
}
})
return (
<div>
{renderContent}
<Slider onChange={this.handleChange} min={0} max={4} defaultValue={this.state.sliderValues} marks={{ 0: 250, 1: 500, 2: 750, 3: 1000, 4: 1500 }} step={null} />
</div>
)
}
}
I only want to show one item from the array at once. Everything works correctly besides on initial render. You'll notice that the component only renders itself once you've interacted with the <Slider />..
Why is this?
Codesandbox
import React, {Component} from "react";
import ReactDOM from "react-dom";
import Slider from 'rc-slider'
import "./styles.css";
const array = ["Hey", "Ho", "Me", "Too", "See"]
class App extends Component {
// you need to provide an initial value to be rendered at the first time
state = { sliderValues: 0 };
handleChange = sliderValues => {
this.setState({ sliderValues }, () => console.log(array[this.state.sliderValues]));
};
render() {
const renderContent = array.map((value, index) => {
console.log(value, 'value')
console.log(index, 'index')
if (index === this.state.sliderValues) {
return (
<div key={value}>
<h1>{value}</h1>
</div>
)
}
})
return (
<div>
{renderContent}
<Slider onChange={this.handleChange} min={0} max={4} defaultValue={this.state.sliderValues} marks={{ 0: 250, 1: 500, 2: 750, 3: 1000, 4: 1500 }} step={null} />
</div>
)
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Related
I'm trying to do unit tests using react-test-renderer and I can't seem to make the test pass even though the underlying component I'm testing works. Can someone tell me what I'm doing wrong
This is the code for the component
import React, { useState } from "react";
import styles from "./styles";
import { View, Pressable, Animated } from "react-native";
import Text from "components/Text/";
import { t } from "services/i18n";
import Form, { Checkbox } from "components/Form";
import WarrantyDetails from "components/WarrantyDetails";
import Icons from "icons";
import { Color } from "theme";
import animation from "./animation";
const WarrantyToggle = ({ checked = false, price = 0, onUpdate = () => { }, disabled = false }) => {
const [detailsVisible, setDetailsVisible] = useState(false);
const [isChecked, setIsChecked] = useState(checked);
const [opacity, scale] = animation(isChecked);
const onChange = (value) => {
onUpdate(value);
setIsChecked(value);
};
return (
<View style={styles.container}>
<Form
style={styles.form}
initialValues={{ warranty: checked }}>
<Checkbox
id="warranty"
name="warranty"
onChange={onChange}
disabled={disabled}
testID="boxcheck"
style={styles.checkbox}
contentStyle={styles.priceRow}
checkbox={() => (
<View style={styles.heartCheckbox}>
<Icons.Warranty size={28} color={Color.dark} strokeWidth={3} />
<Animated.View style={[styles.heart, { opacity: opacity, transform: [{ scale: scale }] }]} >
<Icons.WarrantySelected size={28} color={Color.red} strokeWidth={3} />
</Animated.View>
</View>
)}
label={() => (<Text size="3" font="bold">{t("common:add-protection-plan")}</Text>)}>
<Text style={[styles.price, { opacity: isChecked ? 1 : 0.6 }]}>{`$${price?.toFixed(2)}`}</Text>
<Pressable testID="pressdetails" size={2.5} underline onPress={() => setDetailsVisible(true)}><Text underline style={styles.details}>{t("common:details")}</Text></Pressable>
</Checkbox>
</Form>
<WarrantyDetails
testID = "warrantydetailshigher"
selected={isChecked}
setSelected={setIsChecked}
visible={detailsVisible}
onClose={() => setDetailsVisible(false)}
onChange={onChange}
price={price}
/>
</View>
);
};
export default WarrantyToggle;
This is the code for the test
import React from "react";
import renderer from "react-test-renderer";
import WarrantyToggle from "../../../src/components/WarrantyToggle";
const tree = renderer.create(<WarrantyToggle/>)
test(" Perfect lib error screen renders correctly", () => {
const tree1 = renderer
.create(
<WarrantyToggle name="hello test" />
)
.toJSON();
expect(tree1).toMatchSnapshot();
});
test('box checked' ,() =>{
const warrantycheck = tree.root.findByProps({testID:'boxcheck'})
const detailstate = tree.root.findByProps({testID:'warrantydetailshigher'})
if(detailstate.selected == true){
renderer.act(() => warrantycheck.onChange());
expect(detailstate.selected).toEqual(true)
}
else{
renderer.act(() => warrantycheck.onChange());
expect(detailstate.selected).toEqual(false)
}
});
test('press details' ,() =>{
const warrantypress = tree.root.findByProps({testID:'pressdetails'})
const detailstate = tree.root.findByProps({testID:'warrantydetailshigher'})
renderer.act(() => warrantypress.onPress());
expect(detailstate.visible).toEqual(true)
});
These are the errors I'm getting
FAIL __tests__/components/WarrantyToggle/WarrantyToggle.test.js
● Console
console.error
The above error occurred in the <WarrantyToggle> component:
at WarrantyToggle (/Users/robertbeit/Documents/sally2/app-mobile-sally-reactnative/src/components/WarrantyToggle/index.js:3777:35)
Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.
at logCapturedError (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:10989:23)
at update.callback (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11022:5)
at callCallback (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:3662:12)
at commitUpdateQueue (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:3683:9)
at commitLifeCycles (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11855:11)
at commitLayoutEffects (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:14443:7)
at HTMLUnknownElement.callCallback (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11391:14)
at HTMLUnknownElement.callTheUserObjectsOperation (node_modules/jsdom/lib/jsdom/living/generated/EventListener.js:26:30)
● Perfect lib error screen renders correctly
TypeError: Cannot read properties of undefined (reading 'current')
at iconAnimation (src/components/WarrantyToggle/animation.js:411:83)
at WarrantyToggle (src/components/WarrantyToggle/index.js:3847:54)
at renderWithHooks (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6016:18)
at mountIndeterminateComponent (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8744:13)
at beginWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:9966:16)
at performUnitOfWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:13800:12)
at workLoopSync (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:13728:5)
at renderRootSync (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:13691:7)
at performSyncWorkOnRoot (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:13404:18)
at node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2537:26
at unstable_runWithPriority (node_modules/scheduler/cjs/scheduler.development.js:468:12)
at runWithPriority (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2486:10)
at flushSyncCallbackQueueImpl (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2532:9)
at flushSyncCallbackQueue (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2519:3)
at scheduleUpdateOnFiber (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:13004:9)
at updateContainer (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:16454:3)
at Object.create (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:17149:3)
at Object.<anonymous> (__tests__/components/WarrantyToggle/WarrantyToggle.test.js:8:3)
I tried running yarn test but it fails.
Edit here is the code for the animation file
import { useRef, useEffect, useCallback } from "react";
import { Animated, Easing } from "react-native";
const iconAnimation = (selected) => {
const opacityValue = useRef(new Animated.Value(0)).current;
const scaleValue = useRef(new Animated.Value(0)).current;
const animateOpacityValue = useCallback(
() => {
Animated.timing(opacityValue, {
toValue: selected ? 1 : 0,
easing: Easing.inOut(Easing.quad),
duration: 300,
useNativeDriver: true,
isInteraction: false
}).start();
Animated.timing(scaleValue, {
toValue: selected ? 1.1 : 1,
easing: selected ? Easing.out(Easing.back(25)) : Easing.inOut(Easing.quad),
duration: 500,
useNativeDriver: true,
isInteraction: false
}).start();
},
[selected],
);
useEffect(() => {
animateOpacityValue();
}, [animateOpacityValue]);
return [opacityValue, scaleValue];
};
export default iconAnimation;```
this is from the index.js form file
import React, { useRef, Fragment, useEffect } from "react";
import { View } from "react-native";
import styles from "./style";
import { Formik } from "formik";
function Row({ children, getRef = () => { }, nextElement = () => { }, values, onUpdate, onPressHandler, style }) {
children = children.filter(function (element) {
return element !== undefined;
});
return (
<View style={[styles.row, style]}>
{
React.Children.map(children, (child, index) => {
if (!child || child.props === undefined) {
return <View />;
}
const id = child.props.id;
return (
<Fragment key={`row-${index}`}>
{
React.cloneElement(child, {
containerStyle: styles.rowItem,
getRef: getRef,
value: (values && values[id]) ? values[id] : "",
values: values,
flex: child.props.flex ? child.props.flex : 1,
onUpdate: onUpdate,
onPressHandler: onPressHandler ?? child.props.onPress,
nextElement: nextElement,
})
}
{
index < children.length - 1 &&
<View style={styles.gutter} />
}
</Fragment>
);
})
}
</View>
);
}
function Form({
formRef,
children,
values,
onUpdate,
initialValues,
initialTouched,
validationSchema,
onSubmitHandler,
fieldValueRef,
resetForm = false,
style = {},
gutterStyle = {}
}) {
const internalFormRef = React.createRef(undefined);
useEffect(() => {
if ((formRef && formRef.current) && resetForm) {
formRef.current.resetForm();
} else if ((internalFormRef && internalFormRef.current) && resetForm) {
internalFormRef.current.resetForm();
}
}, [resetForm, formRef, internalFormRef]);
const inputRefs = useRef([]);
const focusNextElement = (event) => {
const index = inputRefs.current.indexOf(event.target);
if (index !== -1) {
const nextElement = inputRefs.current[index + 1];
if (nextElement) {
nextElement.focus();
}
}
};
const getRef = (element, name) => {
if (element && !inputRefs?.current?.includes(element)) {
inputRefs.current.push(element);
}
};
return (
<View style={[styles.formContainer, style]}>
<Formik
innerRef={formRef ?? internalFormRef}
initialValues={initialValues}
initialTouched={initialTouched}
enableReinitialize={true}
validateOnBlur={true}
validateOnChange
validationSchema={validationSchema}
onSubmit={onSubmitHandler}
>
{({ handleSubmit, setFieldValue }) => {
if (fieldValueRef) {
fieldValueRef.current = setFieldValue;
}
return (
<>
{
React.Children.map(children, (child, index) => {
if (!child || child.props === undefined) {
return <View />;
}
return (
<Fragment>
{
React.cloneElement(child, {
nextElement: focusNextElement,
getRef: getRef,
value: (values && values[child.props.id]) ? values[child.props.id] : "",
values: values,
onUpdate: onUpdate,
onPress: child.props.willHandleSubmit ? handleSubmit : child.props.onPress
})
}
{
index < children.length - 1 &&
<View style={[styles.gutter, gutterStyle]} />
}
</Fragment>
);
})
}
</>
);
}}
</Formik>
</View>
);
}
export default Form;
export { Row };
export { default as FormValidationSchema } from "./formValidationSchemas";
export { default as Button } from "./Button";
export { default as Link } from "./Link";
export { default as NumberInput } from "./NumberInput";
export { default as LoadingIndicator } from "./LoadingIndicator";
export { default as Checkbox } from "./Checkbox";
export { default as PLPCheckbox } from "./PLPCheckbox";
export { default as Modal } from "./Modal";
export { default as RadioGroup, RadioButton } from "./RadioGroup";
export { default as SelectInput } from "./SelectInput";
export { default as TextInput } from "./TextInput";
export { default as GooglePlacesInput } from "./GooglePlacesInput";
export { default as DatePicker } from "./DatePicker";
export { default as TextInputButton } from "./TextInputButton";
I think the problem is in animation.js file. Last error line point to animation.js file. Can you share animation.js file
iconAnimation (src/components/WarrantyToggle/animation.js:411:83)
I have a componenet that wraps its children and slides them in and out based on the stage prop, which represents the active child's index.
As this uses a .map() to wrap each child in a div for styling, I need to give each child a key prop. I want to assign a random key as the children could be anything.
I thought I could just do this
key={`pageSlide-${uuid()}`}
but it causes an infinite loop/React to freeze and I can't figure out why
I have tried
Mapping the children before render and adding a uuid key there, calling it via key={child.uuid}
Creating an array of uuids and assigning them via key={uuids[i]}
Using a custom hook to store the children in a state and assign a uuid prop there
All result in the same issue
Currently I'm just using the child's index as a key key={pageSlide-${i}} which works but is not best practice and I want to learn why this is happening.
I can also assign the key directly to the child in the parent component and then use child.key but this kinda defeats the point of generating the key
(uuid is a function from react-uuid, but the same issue happens with any function including Math.random())
Here is the full component:
import {
Children,
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import PropTypes from "prop-types";
import uuid from "react-uuid";
import ProgressBarWithTicks from "./ProgressBarWithTicks";
import { childrenPropType } from "../../../propTypes/childrenPropTypes";
const calculateTranslateX = (i = 0, stage = 0) => {
let translateX = stage === i ? 0 : 100;
if (i < stage) {
translateX = -100;
}
return translateX;
};
const ComponentSlider = ({ stage, children, stageCounter }) => {
const childComponents = Children.toArray(children);
const containerRef = useRef(null);
const [lastResize, setLastResize] = useState(null);
const [currentMaxHeight, setCurrentMaxHeight] = useState(
containerRef.current?.childNodes?.[stage]?.clientHeight
);
const updateMaxHeight = useCallback(
(scrollToTop = true) => {
if (scrollToTop) {
window.scrollTo(0, 0);
}
setCurrentMaxHeight(
Math.max(
containerRef.current?.childNodes?.[stage]?.clientHeight,
window.innerHeight -
(containerRef?.current?.offsetTop || 0) -
48
)
);
},
[stage]
);
useEffect(updateMaxHeight, [stage, updateMaxHeight]);
useEffect(() => updateMaxHeight(false), [lastResize, updateMaxHeight]);
const resizeListener = useMemo(
() => new MutationObserver(() => setLastResize(Date.now())),
[]
);
useEffect(() => {
if (containerRef.current) {
resizeListener.observe(containerRef.current, {
childList: true,
subtree: true,
});
}
}, [resizeListener]);
return (
<div className="w-100">
{stageCounter && (
<ProgressBarWithTicks
currentStage={stage}
stages={childComponents.length}
/>
)}
<div
className="position-relative divSlider align-items-start"
ref={containerRef}
style={{
maxHeight: currentMaxHeight || null,
}}>
{Children.map(childComponents, (child, i) => (
<div
key={`pageSlide-${uuid()}`}
className={`w-100 ${
stage === i ? "opacity-100" : "opacity-0"
} justify-content-center d-flex`}
style={{
zIndex: childComponents.length - i,
transform: `translateX(${calculateTranslateX(
i,
stage
)}%)`,
pointerEvents: stage === i ? null : "none",
cursor: stage === i ? null : "none",
}}>
{child}
</div>
))}
</div>
</div>
);
};
ComponentSlider.propTypes = {
children: childrenPropType.isRequired,
stage: PropTypes.number,
stageCounter: PropTypes.bool,
};
ComponentSlider.defaultProps = {
stage: 0,
stageCounter: false,
};
export default ComponentSlider;
It is only called in this component (twice, happens in both instances)
import { useEffect, useReducer, useState } from "react";
import { useParams } from "react-router-dom";
import {
FaCalendarCheck,
FaCalendarPlus,
FaHandHoldingHeart,
} from "react-icons/fa";
import { IoIosCart } from "react-icons/io";
import { mockMatches } from "../../../templates/mockData";
import { initialSwapFormState } from "../../../templates/initalStates";
import swapReducer from "../../../reducers/swapReducer";
import useFetch from "../../../hooks/useFetch";
import useValidateFields from "../../../hooks/useValidateFields";
import IconWrap from "../../common/IconWrap";
import ComponentSlider from "../../common/transitions/ComponentSlider";
import ConfirmNewSwap from "./ConfirmSwap";
import SwapFormWrapper from "./SwapFormWrapper";
import MatchSwap from "../Matches/MatchSwap";
import SwapOffers from "./SwapOffers";
import CreateNewSwap from "./CreateNewSwap";
import smallNumberToWord from "../../../functions/utils/numberToWord";
import ComponentFader from "../../common/transitions/ComponentFader";
const formStageHeaders = [
"What shift do you want to swap?",
"What shifts can you do instead?",
"Pick a matching shift",
"Good to go!",
];
const NewSwap = () => {
const { swapIdParam } = useParams();
const [formStage, setFormStage] = useState(0);
const [swapId, setSwapId] = useState(swapIdParam || null);
const [newSwap, dispatchNewSwap] = useReducer(swapReducer, {
...initialSwapFormState,
});
const [matches, setMatches] = useState(mockMatches);
const [selectedMatch, setSelectedMatch] = useState(null);
const [validateHook, newSwapValidationErrors] = useValidateFields(newSwap);
const fetchHook = useFetch();
const setStage = (stageIndex) => {
if (!swapId && stageIndex > 1) {
setSwapId(Math.round(Math.random() * 100));
}
if (stageIndex === "reset") {
setSwapId(null);
dispatchNewSwap({ type: "reset" });
}
setFormStage(stageIndex === "reset" ? 0 : stageIndex);
};
const saveMatch = async () => {
const matchResponse = await fetchHook({
type: "addSwap",
options: { body: newSwap },
});
if (matchResponse.success) {
setStage(3);
} else {
setMatches([]);
dispatchNewSwap({ type: "setSwapMatch" });
setStage(1);
}
};
useEffect(() => {
// set matchId of new selected swap
dispatchNewSwap({ type: "setSwapMatch", payload: selectedMatch });
}, [selectedMatch]);
return (
<div>
<div className="my-3">
<div className="d-flex justify-content-center w-100 my-3">
<ComponentSlider stage={formStage}>
<IconWrap colour="primary">
<FaCalendarPlus />
</IconWrap>
<IconWrap colour="danger">
<FaHandHoldingHeart />
</IconWrap>
<IconWrap colour="warning">
<IoIosCart />
</IconWrap>
<IconWrap colour="success">
<FaCalendarCheck />
</IconWrap>
</ComponentSlider>
</div>
<ComponentFader stage={formStage}>
{formStageHeaders.map((x) => (
<h3
key={`stageHeading-${x.id}`}
className="text-center my-3">
{x}
</h3>
))}
</ComponentFader>
</div>
<div className="mx-auto" style={{ maxWidth: "400px" }}>
<ComponentSlider stage={formStage} stageCounter>
<SwapFormWrapper heading="Shift details">
<CreateNewSwap
setSwapId={setSwapId}
newSwap={newSwap}
newSwapValidationErrors={newSwapValidationErrors}
dispatchNewSwap={dispatchNewSwap}
validateFunction={validateHook}
setStage={setStage}
/>
</SwapFormWrapper>
<SwapFormWrapper heading="Swap in return offers">
<p>
You can add up to{" "}
{smallNumberToWord(5).toLowerCase()} offers, and
must have at least one
</p>
<SwapOffers
swapId={swapId}
setStage={setStage}
newSwap={newSwap}
dispatchNewSwap={dispatchNewSwap}
setMatches={setMatches}
/>
</SwapFormWrapper>
<SwapFormWrapper>
<MatchSwap
swapId={swapId}
setStage={setStage}
matches={matches}
selectedMatch={selectedMatch}
setSelectedMatch={setSelectedMatch}
dispatchNewSwap={dispatchNewSwap}
saveMatch={saveMatch}
/>
</SwapFormWrapper>
<SwapFormWrapper>
<ConfirmNewSwap
swapId={swapId}
setStage={setStage}
selectedSwap={selectedMatch}
newSwap={newSwap}
/>
</SwapFormWrapper>
</ComponentSlider>
</div>
</div>
);
};
NewSwap.propTypes = {};
export default NewSwap;
One solution
#Nick Parsons has pointed out I don't even need a key if using React.Children.map(), so this is a non issue
I'd still really like to understand what was causing this problem, aas far as I can tell updateMaxHeight is involved, but I can't quite see the chain that leads to an constant re-rendering
Interstingly if I use useMemo for an array of uuids it works
const uuids = useMemo(
() => Array.from({ length: childComponents.length }).map(() => uuid()),
[childComponents.length]
);
/*...*/
key={uuids[i]}
I want to increment the counter value of an item on click, I've tried to find the solution in the docs and I watched tutorials but I can't find the solution.
FruitCounter.js
import { Fragment, useState } from "react"
import Fruit from "./Fruit"
const data = [
{ id: 1, name: "🍋", counter: 0 },
{ id: 2, name: "🍒", counter: 0 },
]
const FruitCounter = () => {
const [fruits, setFruits] = useState(data)
const clickHandler = (fruit) => {
// Increment 'counter value of clicked item by 1'
}
return (
<Fragment>
{fruits.map((fruit) => {
return (
<Fruit
key={fruit.id}
{...fruit}
clickHandler={() => clickHandler(fruit)}
/>
)
})}
</Fragment>
)
}
export default FruitCounter
Fruit.js
import React from "react"
const Fruit = ({ counter, name, clickHandler }) => {
return (
<button type="button" className="fruit" onClick={clickHandler}>
<p>{counter}</p>
<h2>{name}</h2>
</button>
)
}
export default Fruit
You can try this
const clickHandler = (fruit) => {
setFruits(
fruits.map((x) => {
if (x.id === fruit.id)
return {
...x,
counter: x.counter + 1,
};
return x;
})
);
};
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
I want to animate the depth of the whole Card when the mouse is over it.
I try this (so-so I'm new in React) but I have no idea how to do it:
<Card
linkButton={true}
href="/servicios/"
onClick={Link.handleClick} zDepth={3}
onMouseEnter={this.setState({zDepth={1}})}>
</Card>
Thanks in advance.
5 years later and there is still no correct answer, you do not have to set the component state when it hovers, just use the pseudo-class :hover:
<Card
sx={{
':hover': {
boxShadow: 20, // theme.shadows[20]
},
}}
>
If you want to use styled():
const options = {
shouldForwardProp: (prop) => prop !== 'hoverShadow',
};
const StyledCard = styled(
Card,
options,
)(({ theme, hoverShadow = 1 }) => ({
':hover': {
boxShadow: theme.shadows[hoverShadow],
},
}));
<StyledCard hoverShadow={10}>
<Content />
</StyledCard>
Live Demo
constructor(props) {
super(props);
this.state = { shadow: 1 }
}
onMouseOver = () => this.setState({ shadow: 3 });
onMouseOut = () => this.setState({ shadow: 1 });
<Card
onMouseOver={this.onMouseOver}
onMouseOut={this.onMouseOut}
zDepth={this.state.shadow}
>
Updated #1
Full example
// StyledCard.js
import React, { Component } from 'react';
import { Card } from 'material-ui/Card';
class StyledCard extends Component {
state: {
shadow: 1
}
onMouseOver = () => this.setState({ shadow: 3 });
onMouseOut = () => this.setState({ shadow: 1 });
render() {
return (
<Card
onMouseOver={this.onMouseOver}
onMouseOut={this.onMouseOut}
zDepth={this.state.shadow}
>
{this.props.children}
</Card>
);
}
export default StyledCard;
.
// Container.js
import React from 'react';
import StyledCard from './StyledCard';
const Container = () => [
<StyledCard>Card 1</StyledCard>,
<StyledCard>Card 2</StyledCard>,
<StyledCard>Card 3</StyledCard>,
];
export default Container;
UPDATED #2
With HOC
// withShadow.js
import React from 'react';
const withShadow = (Component, { init = 1, hovered = 3 }) => {
return class extends React.Component {
state: {
shadow: init
};
onMouseOver = () => this.setState({ shadow: hovered });
onMouseOut = () => this.setState({ shadow: init });
render() {
return (
<Component
onMouseOver={this.onMouseOver}
onMouseOut={this.onMouseOut}
zDepth={this.state.shadow}
{...this.props}
/>
);
}
};
};
export default withShadow;
.
// Container.js
import React from 'react';
import { Card } from 'material-ui/Card';
import withShadow from './withShadow';
const CardWithShadow = withShadow(Card, { init: 2, hovered: 4 });
const Container = () => [
<CardWithShadow>Card 1</CardWithShadow>,
<CardWithShadow>Card 2</CardWithShadow>,
<CardWithShadow>Card 3</CardWithShadow>,
];
export default Container;
#Alex Sandiiarov answer didnt work for me. The docs show to use the raised property.
https://material-ui.com/api/card/
class Component extends React.Component{
state = {
raised:false
}
toggleRaised = () => this.setState({raised:!this.state.raised});
render(){
return <Card onMouseOver={this.toggleRaised}
onMouseOut={this.toggleRaised}
raised={this.state.raised}>
...
</Card>
}
}