My component will not display on the page. There are no errors or any other warnings/messages. I console log the value of gameData and all the data is as it should be. Here's the exported function:
export default function AllGameDeals( gameData ){
const dealArr = new Array()
const deals = () => {
gameData.deals.map((deal) => {
const imageSrc = `https://www.cheapshark.com/img/stores/icons/${(parseInt(deal.storeID)-1)}.png`
deal.imageSrc = imageSrc
dealArr.push(deal)
})
return dealArr
}
deals()
return (
<div>
{dealArr.forEach(gameDeal => (
<Box
key={gameDeal.dealID}
display={{ md: "flex" }}
boxShadow="dark-lg"
p={4}
>
<Box flexShrink={0}>
<Image borderRadius="lg"
width={{ md: 40 }}
height={{ md: 20 }}
src={gameData.info.thumb}
alt={gameData.info.title} />
</Box>
<Box>
<Image
src={gameDeal.imageSrc}
alt={gameDeal.storeID} />
</Box>
<Box>
<Text>{gameDeal.price}</Text>
</Box>
</Box>
))}
</div>
)
}
I feel like I am missing something very obvious...
Try changing from dealArr.forEach to dealArr.map.
The reason is that .forEach returns nothing, but .map returns an array.
You should be using .map instead of .forEach because .forEach doesn't actually return a value
Other possible improvements:
Use useEffect to fetch data on mount of the component (instead of fetching everytime)
Use useState to "persist" your state within the component
Related
Update:
I think the problem is that Child 2 components are not rerendering because there are the same props when handleReset() is triggered in parent component.. but how can I make them rerendering? I tried with useEffect but nothing happens.
I'm struggling for several hours with the following issue:
I have a parent component in which I have some values passed from useState to child 1 (CustomerData) then to child 2 (-AddressForm and -CustomerForm).
My problem is that when the handleReset() from parent component is triggered, the values from child2 (AddressForm and CustomerForm) are not updated and I don't understand why. I Think that Child 2 components are not rerendering when I reset the values and somehow I need to pass in some other way the stated in the set hook from handleReset().
If somebody can help me solve this issue please. Here are my components:
Parent component:
function WidgetCustomerData(props): JSX.Element {
const customerDataTest = {
customerFirstName: 'firstNameTest',
customerLastName: 'lastNameTest',
customerPhone: '1313212',
customerEmail: 'test#test.com',
customerAddress: 'Musterstraße',
customerAddressNo: '12',
customerZip: '80335',
customerCity: 'Berlin',
deviceAddress: 'Straße',
deviceAddressNo: '152',
deviceZip: '32214',
deviceCity: 'Hamburg',
};
const [customerData, setCustomerData] = useState({
addressValue: customerDataTest.customerAddress,
addressValueNr: customerDataTest.customerAddressNo,
addressValueZip: customerDataTest.customerZip,
addressValueCity: customerDataTest.customerCity,
customerFirstName: customerDataTest.customerFirstName,
customerLastName: customerDataTest.customerLastName,
customerPhone: customerDataTest.customerPhone,
customerEmail: customerDataTest.customerEmail,
deviceAddress: customerDataTest.deviceAddress,
deviceAddressNo: customerDataTest.deviceAddressNo,
deviceZip: customerDataTest.deviceZip,
deviceCity: customerDataTest.deviceCity
})
const handleClose = () => {
props.handleClose();
}
const handleSubmit = async (e) => {
e && e.preventDefault();
const formUrl = 'https://fetch.mock/widget-customer-data/addCustomerData';
const formData = new FormData(e.target);
for (const [name, value] of formData) {
console.log(`${name}: ${value}`);
}
const response = await fetch(formUrl.toString(), {
method: 'POST',
body: formData,
credentials: 'same-origin',
});
const data = await response.json();
};
const handleReset = () => {
console.log('handleReset ----');
setCustomerData({
addressValue: customerDataTest.customerAddress,
addressValueNr: customerDataTest.customerAddressNo,
addressValueZip: customerDataTest.customerZip,
addressValueCity: customerDataTest.customerCity,
customerFirstName: customerDataTest.customerFirstName,
customerLastName: customerDataTest.customerLastName,
customerPhone: customerDataTest.customerPhone,
customerEmail: customerDataTest.customerEmail,
deviceAddress: customerDataTest.deviceAddress,
deviceAddressNo: customerDataTest.deviceAddressNo,
deviceZip: customerDataTest.deviceZip,
deviceCity: customerDataTest.deviceCity
})
};
return (
<div className="customer-data">
<Dialog
onClose={handleClose}
open={props.open}
aria-labelledby="customized-dialog-title"
PaperProps={{
style: { borderRadius: 20, minWidth: '80%', maxHeight: 'fit-content' },
}}>
<DialogTitle id="customized-dialog-title" onClose={handleClose} />
<Typography style={{ marginTop: 20, paddingLeft: 48, fontSize: 32, fontWeight: 600 }}>Kundendaten bearbeiten</Typography>
{/* <DialogContent> */}
<form onSubmit={handleSubmit}>
<div style={{ paddingLeft: 48, paddingRight:48 }}>
<CustomerData
customerData={customerData}
/>
</div>
<DialogActions>
<ResetButton onClick={handleReset} variant="contained" color="primary">
Reset
</ResetButton>
<SaveButton type="submit" variant="contained" color="primary">
Save
</SaveButton>
</DialogActions>
</form>
{/* </DialogContent> */}
</Dialog>
</div>
);
}
export default WidgetCustomerData;
Child1
export default ({nextStepAction, customerData }: MoleculeSystemManagementCustomerDataProps): JSX.Element => {
return (
<div className='c-data'>
<CustomerForm customerData={customerData} />
<AddressForm formId='customer' customerData={customerData} />
</div>
);
}
Child2
function AddressForm({formId customerData }): JSX.Element {
const classes = useStyles();
const { addressValue, addressValueNr, addressValueZip, addressValueCity, deviceAddress, deviceAddressNo, deviceZip, deviceCity } = customerData;
return (
<Grid container className={classes.borderedRow}>
<Grid item xs={12} sm={6} md={3} className={classes.formColumn}>
<div>
<InputField type='label' id='address' name={`${formId}-address`} value={formId === 'customer' ? addressValue : deviceAddress} label={'Street'} />
</div>
</Grid>
<Grid item xs={12} sm={6} md={3} className={classes.formColumn}>
<InputField type='label' id='city' name={`${formId}-city`} value={formId === 'customer' ? addressValueNr : deviceAddressNo} label={'Number'} />
</Grid>
<Grid item xs={12} sm={6} md={3} className={classes.formColumn}>
<InputField type='label' id='state' name={`${formId}-state`} value={formId === 'customer' ? addressValueZip : deviceZip} label={'ZIP'} />
</Grid>
<Grid item xs={12} sm={6} md={3} className={classes.formColumn}>
<InputField type='label' id='zip' name={`${formId}-zip`} value={formId === 'customer' ? addressValueCity : deviceCity} label={'ORT'} />
</Grid>
</Grid>
);
}
export default AddressForm;
I think the problem is occurring because the state is only being passed to the first child component.
If you look carefully, you'll see that during the passage from the child (1) to child(2) there's no state dealing with it. React is not going to update the value of your props if it's not a state.
Probably, you just need to create a state within the first child. Look the example:
Child 1
export default ({nextStepAction, customerData }: MoleculeSystemManagementCustomerDataProps): JSX.Element => {
const [addressIsDifferent, setAddressIsDifferent] = useState(true);
const [customerDataState, setCustomerDataState] = useState(customerData)
return (
<div className='c-data'>
<CustomerForm customerData={customerData} />
<AddressForm formId='customer' customerData={customerDataState} />
</div>
);
}
Extra tips: I don't what are the use cases of your project, but I highly recommend you to take a look at React Context API
It's a great idea to use it if you need to pass data through components that need the same data and do cohesive functions.
In a typical React application, data is passed top-down (parent to child) via props, but such usage can be cumbersome for certain types of props (e.g. locale preference, UI theme) that are required by many components within an application. Context provides a way to share values like these between components without having to explicitly pass a prop through every level of the tree.
it looks like you are setting the state to the same data as it was initialised. you initialise it with customerDataTest and then set it to customerDataTest. same thing
The state of a value set using React useState hook gets set to the proper value and then reset to null. Critical code below. The click event that sets the startDate to the current date and time is 3 components down from where startDate is initialized. When setStartDate did not work I created an arrow function, updateStartDate. Both had the same problem where the startDate was changed after the click event (witnessed per the console.log in the top component), but was null just before the next click event (per the console.log in the click event). This is not an async problem as I see the change made before subsequent click.
If this is something that just does not work please explain. I could probably fix with useReducer but prefer to keep the useState if there is something I can do to correct this... If not correctable then I would like to at least understand why it does not work so that I can avoid this problem in the future.
export const DisplayTicTacToeContainer = (props) => {
const [startDate, setStartDate]= useState();
const updateStartDate = (newDate) => {
setStartDate(newDate);
}
useEffect (() => {
setStartDate(null);
}, []);
useEffect(() => {
console.log( "displayTicTacToeContainer useEffect for change of startDate = ", startDate)
}, [startDate]);
return (
<DisplayTicTacToeMatch arrayOfMatchingItems ={arrayOfMatchingItems}
startDate={startDate}
setStartDate={setStartDate}
updateStartDate={updateStartDate}
/>);
}
//-----------------------------------------------
export const DisplayTicTacToeMatch = (props) => {
const { startDate,
setStartDate,
updateStartDate,
} = props;
useEffect(() => {
// Performs some prep and working fine.
}, []);
return (
<TicTacToe
startDate={startDate}
setStartDate={setStartDate}
updateStartDate={updateStartDate}
/>
);
}
//-----------------------------------------------
const TicTacToeContainer = (props) => {
const { startDate,
setStartDate,
updateStartDate,
} = props;
const [board, setBoard] = useState(<Board
updateStartDate={updateStartDate}
startDate={startDate}
setStartDate={setStartDate}/>);
return (
<Board/>
)
}
export default TicTacToeContainer;
I renamed the component to BoardComponent and the state variable to boardLayout. I included the full return portion of the BoardComponent below.
As I am still experiencing the problem I would agree with you that, "DisplayTicTacToeContainer is being mounted twice". Any thoughts on how I can avoid this from happening?
Other than this inability to setStartDate, everything is working fine.
//-----------------------------------------------
const Board = (props) => {
const { updateStartDate,
startDate,
setStartDate,
} = props;
return (
<>
<Grid container maxwidth="lg" alignItems="center" spacing={1}>
<Grid item xs={9}>
<Grid container alignItems="center">
<Grid item xs={9}>
<Typography variant = "body1">
First select a square. Once the "Inquiry" word or phrase appears below, find
the correct response in the column on the right and select that buttton. A correct
response will fill the square previously selected with an "O" or "X".
</Typography>
<div style={{ width: '100%' }}>
<Box
display="flex"
flexWrap="wrap"
p={1}
m={1}
bgcolor="background.paper"
css={{ maxWidth: 900 }}
>
<Box p={1} bgcolor="grey.300">
Inquiry : {inquiry}
</Box>
</Box>
<Box
display="flex"
flexWrap="wrap"
p={1}
m={1}
bgcolor="background.paper"
css={{ maxWidth: 900 }}
>
<Box p={1} bgcolor="grey.300">
Next move by : {currentPlayer}
</Box>
<Box p={1} bgcolor="grey.300">
{showStatus}
</Box>
</Box>
</div>
</Grid>
</Grid>
<MyAux>
{boardLayout.map((row, rowId) => {
const columns = row.map((column, columnId) => (
<Grid key={columnId} item>
<ButtonBase >
<Paper
onClick={(e) => {
clickSquareHandler(e);
}}
elevation={4}
data-coord={rowId + ':' + columnId}
id={"Square" + rowId.toString() + columnId.toString()}
className={classes.Paper}>
<Icon
className={classes.Icon}
style={{fontSize: 78}}>
</Icon>
</Paper>
</ButtonBase>
</Grid>
));
return (
<Grid
key={rowId}
className={classes.Grid}
container
spacing={2}>
{columns}
</Grid>)
})}
</MyAux>
</Grid>
<Grid item xs={3} >
<Paper className={classes.paper}>
<Typography variant = "body1">
Response Options
</Typography>
<ButtonGroup
orientation="vertical"
color="secondary"
aria-label="vertical outlined secondary button group"
>
{responseChoices.map((choice) => (
<Controls.Button
key ={choice.value}
text={choice.value}
variant="contained"
color = "secondary"
onClick={() => {
chooseChecker(choice);
}}
className={
response && response.value === choice.value ? "selected" : ""
}
disabled={!!selected[choice.value]}
fullWidth = "true"
size = "small"
/>
))}
</ButtonGroup>
</Paper>
</Grid>
</Grid>
</>
)
}
BoardContainer.propTypes = {
won: PropTypes.func,
size: PropTypes.number
};
export default BoardContainer;
At least, code below doesn't make much sense.
Please don't set state value as a component.
Also, try to name state variable different from components, since it will confuse you at some ppint.
const [board, setBoard] = useState(<Board
updateStartDate={updateStartDate}
startDate={startDate}
setStartDate={setStartDate}/>);
return (
<Board/>
)
Another possibility is that the DisplayTicTacToeContainer is being mounted twice, but I can't confirm it with the code provided.
In React-Native I am using this component <SimpleLineIcons name='heart' size={32} style={{position: 'absolute', left: 10}}/> I want so that when I click on it, it gets replaced with this component <AntDesign name='heart' size={32} color="red" style={{position: 'absolute', left: 10}}/>. How can I do this?
Create an item in the state, called toggleIcon. This will dictate what component to display. Then, to conditionally render the two component simply use a ternary operator like this:
export const MockComponent = () => {
const [toggleIcon, setToggleIcon] = React.useState(false);
return (
<>
// ...
{toggleIcon ? <SimpleLineIcon onClick={() => setToggleIcon(!toggleIcon)} /> : <AntDesign <SimpleLineIcon onClick={() => setToggleIcon(!toggleIcon)} />}
</>
)
}
I am developing a small project in React Native. I have noticed a weird situation whereby when I render a list through a helper function, I get the eponymous error:
Objects are not valid as a React child
Now this error normally means that I am trying to render an object, which is not the case. I will paste two snippets of code. The first one is how I render the data through a helper function, resulting through an error. The second snippet is how I render the data directly in the render() method and working successfully.
Snippet #1: Rendering through helper function renderUsers() -> does not work
renderUsers = async () => {
return this.props.userList.map(
({ instructions, address, createdDate, _id }) => (
<Card
title={`${moment(createdDate).format("YYYY-MM-DD HH:mm")}`}
key={_id}
>
<Text style={{ marginBottom: 10 }}>{instructions}.</Text>
<Button backgroundColor="#03A9F4" title="Ready to Help!" />
</Card>
)
);
};
...
render() {
return this.props.isFetchingUsers ? null : (
<View style={{ flex: 1 }}>
<ScrollView>
{this.renderUsers()}
</ScrollView>
</View>
);
}
Snippet #2: Rendering directly inside render() function -> works OK
render() {
return this.props.isFetchingUsers ? null : (
<View style={{ flex: 1 }}>
<ScrollView>
{this.props.userList.map(
({ instructions, address, createdDate, _id }) => (
<Card
title={`${moment(createdDate).format("YYYY-MM-DD HH:mm")}`}
key={_id}
>
<Text style={{ marginBottom: 10 }}>{instructions}.</Text>
<Button backgroundColor="#03A9F4" title="Ready to Help!" />
</Card>
)
)}
</ScrollView>
</View>
);
}
What could be the reason?
Your snippet 1 should be like this.
renderUsers = () => {
return this.props.userList.map(
({ instructions, address, createdDate, _id }) => (
<Card
title={`${moment(createdDate).format("YYYY-MM-DD HH:mm")}`}
key={_id}
>
<Text style={{ marginBottom: 10 }}>{instructions}.</Text>
<Button backgroundColor="#03A9F4" title="Ready to Help!" />
</Card>
)
);
};
...
render() {
return this.props.isFetchingUsers ? null : (
<View style={{ flex: 1 }}>
<ScrollView>
{this.renderUsers()}
</ScrollView>
</View>
);
}
You need to remove the keyword async
The async function will return Promise Object, which is not supposed to be a React child.
But you no need async function for Array map.
If you want to render something asynchronously try updating state by this.setState and render it accordingly.
You shouldn't return null in render method!
You should render an element like this:
render() {
<View style={{ flex: 1 }}>
<ScrollView>
{
(!this.props.isFetchingUsers && this.props.userList) &&
this.renderUsers()
}
</ScrollView>
</View>
}
then remove the keyword async from renderUsers method.
I have the following react component
const Layout = () => {
return (
<ThemeProvider>
<Flex wrap>
<Box width={[1, 1 / 3]}>
<Text bold>Targeting</Text>
<Iconlist text="Something"/>
</Box>
<Box width={[1, 1 / 3]}>
<Text bold>Tactics</Text>
</Box>
<Box width={[1, 1 / 3]}>
<Text bold>Results</Text>
</Box>
</Flex>
</ThemeProvider>
)
};
export default Layout;
I want to implement something like this:
...
<Iconlist text="Something"/>
<Iconlist text="Something else"/>
<Iconlist text="Something else else"/>
<Iconlist text="Something ...."/>
...
How can I write a loop that can do the above i.e. display multiple Iconlist component. I know how to use props to change the "something" value but I am unable to run a loop.
I tried this:
<Box width={[1, 1 / 3]}>
<Text bold>Targeting</Text>
{
for(var i=0;i<=5;i++){
<Iconlist text="Something"/>
}
}
</Box>
But I am guessing that's not the correct way to inject javascript in between. I am a beginner to React and trying to learn how to do this. What's the best practice?
You can use Array.prototype.map()
The map() method creates a new array with the results of calling a
provided function on every element in the calling array.
Example
<Box width={[1, 1 / 3]}>
<Text bold>Targeting</Text>
{ Array(5).map((i) => (<Iconlist key={i} text={`Something ${i}`} />)) }
</Box>
<Box width={[1, 1 / 3]}>
<Text bold>Targeting</Text>
{
['something',
'something else',
'something else else',
'something ...',
].map((text, index) => <Iconlist key={index} text={text}/>)
}
</Box>
You need first to declare the text values in array like this
const texts = ['something', 'something else' ..]
And in the render method add the following
{ texts.map((text) => <Iconlist text={text}/>) }
You can store your items in an array in the state:
...
constructor(props) {
super(props)
this.state = {
items: [
'something',
'something else',
'something else else',
'something ...'
]
}
}
...
...and then map over the array in your render function:
...
{
this.state.items.map(item => <Iconlist text={ item } />)
}
...
This will then re-render when you update the state.
You could also pass in the array as a prop and then render like this:
...
{
this.props.items.map(item => <Iconlist text={ item } />)
}
...