Using React, Next.JS and React-Bootstrap
So, i'm trying to just create a dismissable alert, like in the React-Bootstrap documentation, however, it would not close for me. Upon further inspection (that's what the console.logs arefor), i noticed that my Alert component is lauching "onClose" immediately upon being opened. That's a problem. Further more, i've also noticed that no matter what i pass as "onClosing", it reads in the console as "undefined", rather than outputting the function i sent it. This is made further weird, by the fact that just two lines down, im sending the same function, with opposite state to another component (where i signal the website to open the alert), and it's working completely fine. I've been at this for a couple hours now, and i'm pretty stuck. Any help would be appreciated!
My state variable at init
const [showAlert, setShowAlert] = useState(false)
Here's my Alert component
import {Alert, Button, ButtonGroup, ButtonToolbar, Collapse} from 'react-bootstrap'
import PropTypes from 'prop-types'
const MyAlert = ({onClosing, alertHeading, alertText, alertVariant, methodToExecute, buttonText}) => {
console.log(onClosing)
return (
<Alert variant={alertVariant} onClose={()=> onClosing} dismissible style={{margin:'1rem'}}>
<Alert.Heading>{alertHeading}</Alert.Heading>
<p>{alertText}</p>
<ButtonToolbar>
<ButtonGroup style={{margin:'.5rem .5rem .5rem 0'}}>
<Button variant={alertVariant} onClick={()=> {methodToExecute!=undefined ? methodToExecute : onClosing}}>{buttonText}</Button>
</ButtonGroup>
<ButtonGroup style={{margin:'.5rem'}}>
<Button variant={alertVariant} onClick={() => onClosing}>Close</Button>
</ButtonGroup>
</ButtonToolbar>
</Alert>
)
}
MyAlert.defaultProps = {
buttonText: 'OK'
}
/* MyAlert.propTypes = {
onClosing: PropTypes.func
} */
export default MyAlert
And here's my Implementation of it
{showAlert ? <MyAlert onClosing={() => setShowAlert(false), console.log("closing")} alertVariant="danger" alertHeading="Test Alert" alertText="This is just a test alert."/> : ''}
The other component implementation i'm sending that setShowAlert to
<ParameterList onRandomList={() => randomListOfInstruments()} onNewList={ () => addNewInstruments()} onClear={() => setShowAlert(true)}></ParameterList>
Your usage of MyAlert component is probably the issue here:
{showAlert ? <MyAlert onClosing={() => setShowAlert(false), console.log("closing")} alertVariant="danger" alertHeading="Test Alert" alertText="This is just a test alert."/> : ''}
You are passing a value to the onClosing, alertHeading, alertText, alertVariant props of MyAlert, while the actual props of MyAlert are:
{onClosing, alertHeading, alertText, alertVariant, methodToExecute, buttonText}
Among those, you also have methodToExecute, which you are using as a condition when loading your alert:
<Button variant={alertVariant} onClick={()=> {methodToExecute!=undefined ? methodToExecute : onClosing}}>{buttonText}</Button>
Basically, since your methodToExecute is always undefined, this button will always activate onClosing when clicked.
The solution is to add all the necessary props when using MyAlert, or at least include methodToExecute function in the props you pass to it, so your button will bind that to the onClick function instead.
As for onClosing which you are passing as a prop to MyAlert, you also need to fix that, because you are calling two functions separated by comma ',' on its implementation:
onClosing={() => setShowAlert(false), console.log("closing")}
The proper implementation would be:
onClosing={() => {
setShowAlert(false);
console.log("closing");
}}
Related
I posted this here because I am relatively new to React and didn't know what exactly should I google. In my React project, I have a kendo grid that has a custom column named OPTIONS, like this:
<Grid onDataStateChange={onDataStateChange}
data={result}
{...{skip:0, take:13}}>
<GridColumn cell={CommandCell} title="Options"/>
<GridColumn field="session_id" title="Session" filter='text'/>
<GridColumn field="sn_zag_id" title="Service" filter='text'/>
The Option column is defined like this:
const [visible2, setVisible2] = useState(false);
const [snZagId, setSnZagId] = useState();
const toggleDialogPrilog = (props) => {
setVisible2(!visible2);
setSnZagId(props.dataItem.sn_zag_id)
}
const CommandCell = (props) => <Options {...props}/>
const Options= (props) => {
return <td className="k-command-cell">
<div style={{marginTop:'2%'}}>
<Button style={{width:'8vw',marginTop:'2%'}}
onClick={()=>toggleDialogPrilog(props)}>
Add
</Button></>}
</div>
{ visible2 &&
<Dialog onClose={()=> toggleDialogPrilog()} title={"Add"} style={{width:'50%'}}>
<Prilog snZagId={snZagId}/>
</Dialog>
}
</td>;}
So, In the option column I have a button ADD that, when it's clicked, opens a Dialog with PRILOG component inside it. The grid that I am talking about is big, made up of pages of 13 rows. Everything works perfectly, so when I click on the Add button, the dialog is open with custom material for that row. But the thing is, if I open the console/inspect, I can see that when I click add, 13 dialogs are open at the same time:
I am aware to some point that when I click Add, all dialogs are rendered bcz I send props, but I don't know how to stop it. In other words, how can I modify my code so that only one(1) dialog opens when I click Add?
I managed to solve the problem somehow, but I don't know what exactly is the difference. Instead of putting the Options component in the same jsx file, I made another component named SessionOptions like this:
Session.jsx:
import SessionOptions from '../../Popup/SesijaOpcije';
...
const CommandCell = (props) => <SessionOptions props={props}/>;
...
SessionOptions.jsx:
...
export default function SessionOptions({props}) {
...
return <td className="k-command-cell">
<div style={{marginTop:'2%'}}>
<Button style={{width:'8vw',marginTop:'2%'}}
onClick={()=>toggleDialogPrilog(props)}>
Add
</Button></>}
</div>
{ visible2 &&
<Dialog onClose={()=> toggleDialogPrilog()} title={"Add"} style={{width:'50%'}}>
<Prilog snZagId={snZagId}/>
</Dialog>
}
</td>;}
And now it opens just one dialog. The only difference that I clearly see is in sending the props
//Before:
const CommandCell = (props) => <Options {...props}/>
//After:
const CommandCell = (props) => <SessionOptions props={props}/>;
The first one is property spread notation, and the second one is...? Can anybody explain the difference.
If anybody could clearify more.
I am creating a flashcard app, already have a page where I use .map() to render all items. Now I created another page so I can individually show one card at a time, so instead of using .map I used items[x] so I can increment by one with a onClick button. When I first save the file the live update renders fine as predicted, yet if I reload the page it crashes saying that it cannot read the word property of undefined. Now after doing some debugging I found that If I console log the array at first it shows up as zero/0 then prints out again with the info, even though I call the useSelector function to gather the info from the state before trying to access the data. Now this happens no matter if I use the .map() function but for some reason the .map() function does not crash and render fine as expected.
import React from "react";
import FlippableFlashcard from "../FlippableFlashcards/FlippableCard/FlippableCard.jsx";
import { Button, Container, Box } from "#mui/material";
import ArrowRightIcon from "#mui/icons-material/ArrowRight";
import ArrowLeftIcon from "#mui/icons-material/ArrowLeft";
import { useSelector } from "react-redux";
import useStyles from "./styles.js";
const Practice = () => {
const classes = useStyles();
const items = useSelector((state) => state.posts);
const handleAdd = () => {};
return (
<>
<Container className={classes.centerDiv}>
<FlippableFlashcard item={console.log(items[0])} />
{/*//This fails, but this does not <FlippableFlashcard item={items.map((item) => console.log(item))} />*/}
</Container>
<Box textAlign="center" className={classes.ButtonBar}>
<Button onClick={handleAdd}>
<ArrowLeftIcon />
</Button>
<Button>
<ArrowRightIcon />
</Button>
</Box>
</>
);
};
export default Practice;
specifically this <FlippableFlashcard item={console.log(items[0])} />
Error:
FlippableCard.jsx:20 Uncaught TypeError: Cannot read properties of undefined (reading 'word')
So I know it is because of that first console log being 0/null/undefined. Tried doing { items && items[0] } as I saw some saying this would work but did not.
Thanks to Akshay Mathur, I just simply did, works great, basically checks if data exists if not then simply console logs fetching, on the second auto reload it will render.
{items.length ? (
<FlippableFlashcard item={items[x]} key={items[x]._id} />
) : (
console.log("fetching data")
)}
Do something like this:
{items.length > 0 && items.map((item,index)=>(<FlippableFlashcard item={item} key={index} />)}
Been stuck on debugging this for quite a while. I'm trying to have a group of items change onClick but with the use of transform but 'style' is undefined. I've also included the Card component functions. Help would be greatly appreciated
import React,{useRef} from 'react';
import { Card } from '../components';
import { CardItemContainer } from './card-item';
export function CardContainer()
{
const listRef=useRef()
const handleClick=(direction)=>
{
if(direction==="left")
{
listRef.current.style.transform=`translate(230)`
}
}
return(
<Card>
<Card.ListTitle> Continue to watch</Card.ListTitle>
<Card.Wrapper >
<Card.ArrowSliderLeft onClick={()=>handleClick('left')}/>
<Card.List ref={listRef}>
<CardItemContainer index={0}/>
<CardItemContainer index={1}/>
<CardItemContainer index={2}/>
<CardItemContainer index={3}/>
<CardItemContainer index={4}/>
<CardItemContainer index={5}/>
<CardItemContainer index={6}/>
</Card.List>
<Card.ArrowSliderRight onClick={() => handleClick("right")}/>
</Card.Wrapper>
</Card>
)
}
Card Components
import {ArrowBackIosOutlined,ArrowForwardIosOutlined} from "#material-ui/icons";
import React, {} from 'react';
import {
Container,
List,
ListTitle,
Wrapper,
ArrowSliderLeft,
ArrowSliderRight
} from './styles/card';
export default function Card({ children, ...restProps }) {
return <Container {...restProps}>{children}</Container>
}
Card.ListTitle=function CardListTitle({children,...restProps})
{
return <ListTitle{...restProps}> {children} </ListTitle>
}
Card.Wrapper=function CardWrapper({children,...restProps})
{
return <Wrapper{...restProps} > {children} </Wrapper>
}
Card.List=function CardList({children,...restProps})
{
return <List{...restProps} >{children}</List>
}
Card.ArrowSliderLeft = function HeaderArrowBackIosOutlinedSymbol({...restProps })
{
return <ArrowSliderLeft {...restProps }>
{/*id allows me to style the icon directly */}
<ArrowBackIosOutlined id="sliderLeft"/>
</ArrowSliderLeft>
}
Card.ArrowSliderRight = function HeaderArrowForwardIosOutlinedSymbol({...restProps}) {
return (
<ArrowSliderRight {...restProps}>
<ArrowForwardIosOutlined id="sliderRight"/>
</ArrowSliderRight>
);
};
Ignore:
Been stuck on debugging this for quite a while. I'm trying to have a group of items change onClick but with the use of transform but 'style' is undefined. I've also included the Card component functions. Help would be greatly appreciated
Function components like CardList don't have a ref property, only class components or DOM elements do.
You haven't posted List component's implementation, but let's assume it has a <ul> tag, and that is what you eventually need to manipulate its .style.transform
CardList >>> List >> ul (this is the element you need to pass the ref)
To pass the listRef all the way to ul from CardList you need to use the forwardRef technique.
Card.List=React.forwardRef(function CardList (props,ref)
{
const {children,...restProps} = props
return <List{...restProps} ref={ref} >{children}</List>
})
the List component itself :
const List = React.forwardRef(function (props,ref) {
return <ul ref={ref}>
... the implementation of your List
Now you can pass listRef in here and it goes down the chain:
<Card.List ref={listRef}>
Side Note: taking from Drew Reese's comment on this answer, since CardList is just transfering the same props from a parent component to List, you can simply assign List to Card.List, then only one step of ref forwarding would be enough:
Card.List = List // CardList component isn't used anymore.
The same thing could work for Card.ListTitle and Card.Wrapper:
Card.ListTitle=ListTitle
Card.Wrapper=Wrapper
I too have just faced this same issue, and have tried to get my code working again. Checking similarity between your given code and my erroneous code snippet helped me fix the error.
Strangely, I have faced this error with a JSX multi-line comment in place after my element (MUI <Snackbar> element, in my case).
Error(s):
My code snippet looked something like:
<Snackbar open={snackbarOpen} autoHideDuration={5000} onClose={()=>setSnackbar(false)} > {/* My Comment Here */}
<>...</>
</Snackbar>
Quite similar place of JSX comment as your Card Component
Card.ArrowSliderLeft = function
...
return <ArrowSliderLeft {...restProps }>
{/*id allows me to style the icon directly */}
<ArrowBackIosOutlined ... />
</ArrowSliderLeft>
Removing just the comment part {/* */} immediately following an opening tag worked for me.
So, try removing your JSX comment or placing it elsewhere,and see if it helps.
Sharing it here just for my and others future reference. :)
pure-react-carousel gives me an unstyled html button (ButtonBack) and I want to style it using material-ui.
Placing buttons inside buttons is not permitted.
What works is to assign the className prop manually:
<ButtonBack className={"MuiButtonBase-root MuiButton-root MuiButton-contained"}>
<NavigateBeforeIcon />
</ButtonBack>
But it feels wrong, and also does not render as nice as an real Mui-Button.
Of course I could write my own css that mimics Muis but that feels wrong too.
Is there an easier/straight-forward way to this problem?
import { ButtonFirst } from 'pure-react-carousel';
import Button from '#material-ui/core/Button';
const CustomButtonFirst = React.forwardRef((props, ref) => <Button component={ButtonFirst} ref={ref} {...props} />);
// This line is needed because the "disabled" is internal state of "ButtonFirst".
export default withStore(CustomButtonFirst, state => ({ disabled: state.currentSlide === 0 }));
Taken from:
https://github.com/express-labs/pure-react-carousel/issues/175
I'm trying to create a feature to easily hide/show all items (subcomponents). By using useState I am able to set whether or not all items are hidden/shown. By using useEffect I am able to toggle items that are hidden/shown. I'm having issues accessing the props in the subcomponent to determine whether or not a an item has already been expanded. I wish I could explain this better, but hopefully this coding example will paint a better picture.
index.js
import React, { useState } from "react";
import ReactDOM from "react-dom";
import "semantic-ui-css/semantic.min.css";
import { Button } from "semantic-ui-react";
import Item from "./Item";
const Services = props => {
const [allExpanded, setAllExpanded] = useState(false);
return (
<>
<p>
<Button onClick={() => setAllExpanded(false)} content="Hide all" />
<Button onClick={() => setAllExpanded(true)} content="Show all" />
</p>
<p>
<Item expanded={allExpanded} />
<Item expanded={allExpanded} />
<Item expanded={allExpanded} />
</p>
</>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<Services />, rootElement);
Item.js
import React, { useState, useEffect } from "react";
import { Accordion } from "semantic-ui-react";
const Item = props => {
const [expanded, setExpanded] = useState(props.expanded);
useEffect(() => {
setExpanded(props.expanded);
}, [props.expanded]);
return (
<Accordion styled>
<Accordion.Title
onClick={() => {
setExpanded(!expanded);
}}
>
<p>{expanded ? "- Hide Item" : "+ Show Item"}</p>
</Accordion.Title>
<Accordion.Content active={expanded}>Lorem ipsum...</Accordion.Content>
</Accordion>
);
};
export default Item;
CodeSandbox
To replicate my current bug, click any "+ Show Item", then click "Hide All". It will not hide everything, however clicking "Show All", then "Hide All" will hide everything.
You're facing this issue because your parent component actually has three possible states:
All expanded
All collapsed
Neither all expanded or collapsed
To reflect the third state, you could use null/undefined (and pass the setter down into your children components).
Updated example here: https://codesandbox.io/s/competent-villani-i6ggh
Since you are handling the expanded state of your accordions on the top level, I suggest you just pass down the expanded state and the 'toggler' to your items. index.js will handle the logic and your Item component will be presentational.
Here's a fork of your CodeSandbox.
It doesn't look great and probably the item state and toggling can (and probably should) be moved elsewhere (for example a separate reducer with the usage of the useReducer hook)
If you are planning to create these components dynamically, IMO this is the easiest way to go.
If you still want to go your way, you can refactor your Item to a class component and use Refs to get their current state, however I not recommend this approach.
Hope this helps!
Here's a codeandsandbox, forked from yours:
https://codesandbox.io/s/competent-wildflower-n0hb8
I changed it so that instead of having something like this:
let [allExpanded, setAllExpanded] = useState(true)
You have something like this:
let [whichExpanded, setWhichExpanded] = useState({0: true, 1:true, 2: true})
Then, on for your callback to expand/collapse all buttons, you have this:
<button onClick=()=>{
let newState = {}
for(let order in whichEpanded){
newState[order] = false //change every key to false
}
setAllExpanded(newState)
}> hide all</button>
Then, I passed down an "order" prop to your items. The "order" prop is used as an argument to a callback function that I pass down, so when you click on each item, it updates the whichExpanded state, to toggle the visibility of just that one item.
// pass this to eac of the items:
const setIndividualItemExpanded = order => {
let newItemsExpandedState = { ...itemsExpanded };
newItemsExpandedState[order] = !newItemsExpandedState[order];
setItemsExpanded(newItemsExpandedState);
};
Each item component:
<Item
expanded={itemsExpanded[0]} //is reading from the state
order={0}
setExpanded={setIndividualItemExpanded}
/>
Then, you can remove the useState from the rendered component and just update with the "setExpanded" prop
(See complete code in codesandbox pasted at top)