I currently have this bit of code, that is working, but it is throwing a Warning: Each child in a list should have a unique "key" prop.
Check the render method of `TopMenuDropdown`. See https://reactjs.org/link/warning-keys for more information.
MenuItem#http://localhost:3000/protis-react/static/js/vendors~main.chunk.js:2815:110
TopMenuDropdown#http://localhost:3000/protis-react/static/js/main.chunk.js:2432:5
div
div
App#http://localhost:3000/protis-react/static/js/main.chunk.js:63:5
Router#http://localhost:3000/protis-react/static/js/vendors~main.chunk.js:174391:30
BrowserRouter#http://localhost:3000/protis-react/static/js/vendors~main.chunk.js:174011:35
at me. I am not even sure where to track down what is missing a key, or where I missed putting it in. Any way for me to track it, or is someone here able to find the missing elements that need the unique keys. I tried to put a key on almost anything I generated originally, but I'm not sure what went wrong where, and this has been bothering me.
import React from 'react';
import {Menu, MenuButton, MenuDivider, MenuItem, SubMenu} from '#szhsin/react-menu';
import '#szhsin/react-menu/dist/index.css'
const tooltipStyle = {
position: "absolute",
pointerEvents: "none",
backgroundColor: "#D7D7A6",
border: "1px solid",
padding: "1px 8px",
whiteSpace: "nowrap",
zIndex: 200
};
class TopMenuDropdown extends React.Component {
state = {
pos: {x: 0, y: 0},
tooltip: '',
show: false
};
createTooltip = (tooltip) => ({
onMouseEnter: ({clientX, clientY}) => {
this.setState(
{
pos: {x: clientX, y: clientY},
tooltip: tooltip,
show: true
}
);
},
onMouseLeave: () => {
this.setState(
{
show: false
}
)
}
});
handleSelect = ({value}) => {
this.props.onClick(value);
}
renderMenuItem(menuAnchor, menuItems, isSubMenu) {
if (isSubMenu) {
return (
<SubMenu key={menuAnchor.id}
label={
<div
key={menuAnchor.id}
{...this.createTooltip(menuAnchor.attributes.title)}
>
{menuAnchor['#text']}
</div>
}
>
{this.menuGeneration(menuItems, menuAnchor)}
</SubMenu>
);
}
return (
<>
<Menu
style={{display: 'flex', float: 'left'}}
key={menuAnchor.id}
menuButton={
<MenuButton
key={menuAnchor.id}
{...this.createTooltip(menuAnchor.attributes.title)}>
{menuAnchor['#text']}
</MenuButton>}
onChange={({open}) => !open && this.setState({show: false})}
>
{this.menuGeneration(menuItems, menuAnchor)}
</Menu>
{this.state.show && (
<div
key={menuAnchor.id}
style={{
...tooltipStyle,
left: this.state.pos.x,
top: this.state.pos.y
}}
>
{this.state.tooltip}
</div>
)}
</>
);
}
menuGeneration(menuItems, menuAnchor) {
if (menuItems === undefined) {
return <></>;
}
if (!Array.isArray(menuItems)) {
menuItems = [menuItems];
}
return (
menuItems.map(({a, attributes, ul}) => {
if (ul !== undefined && ul.li !== undefined) {
return (
this.renderMenuItem(a, ul.li, true)
);
}
if (a === undefined) {
return (
<MenuDivider key={menuAnchor.id} />
)
}
return (
<MenuItem
key={menuAnchor.id}
value={a.attributes.id}
onClick={(id) => this.handleSelect(id)}
{...this.createTooltip(a.attributes.title)}
>
{a['#text']}
</MenuItem>)
}
)
)
}
render() {
if (!this.props.topMenu.hasOwnProperty('ul')) {
return null;
}
const menuItemRendering = this.props.topMenu.ul.li.map(({a, ul}) => {
return this.renderMenuItem(a, ul.li, false);
});
return (
<div style={{display: 'flex'}}>
{menuItemRendering}
</div>
)
}
}
export default TopMenuDropdown;
Issue is in renderMenuItem in the "else" branch when you render Menu and a div into a Fragment. When mapping JSX a React key needs to be on the outermost returned mapped element, the Fragment in this case.
renderMenuItem(menuAnchor, menuItems, isSubMenu) {
if (isSubMenu) {
return (
<SubMenu
...
>
{this.menuGeneration(menuItems, menuAnchor)}
</SubMenu>
);
}
return (
<Fragment key={menuAnchor.id}> // <-- add missing React key here
<Menu
...
>
{this.menuGeneration(menuItems, menuAnchor)}
</Menu>
{this.state.show && (
<div
...
>
{this.state.tooltip}
</div>
)}
</Fragment>
);
}
Try replacing key={menuAnchor.id} with key={a.id} for items directly in menuGeneration:
if (a === undefined) {
return <MenuDivider key={a.id} />
}
return (
<MenuItem
key={a.id}
value={a.attributes.id}
onClick={(id) => this.handleSelect(id)}
{...this.createTooltip(a.attributes.title)}
>
{a['#text']}
</MenuItem>
)
Related
I'm trying to add an active class on the map element with hover. Everything is perfect but I need to add an active class on the first div when I do not hover over any.
Here is my code...
{WhatWeOfferData.map(({ img, title, para}, index) => {
return (
<div
className={`${style.card} ${addActive.index === index && addActive.show ? `${style.active}` : ""}`}
onMouseEnter={hoverOn}
onMouseLeave={hoverOff}
key={index}
data-id={index}
>
<Image src={img} alt="offer_images" width={37} height={41} />
<h2>{title}</h2>
<p>{para}</p>
</div>
);
})}
and
let [addActive, setAddActive] = useState(false);
const hoverOn = (e) => {
setAddActive({
index: parseInt(e.currentTarget.dataset.id),
show: true
});
};
const hoverOff = (e) => {
setAddActive({
index: parseInt(e.currentTarget.dataset.id),
show: false
});
};
Simply do this:
{
WhatWeOfferData.map(({ img, title, para}, index) => {
return (
<div className={`${style.card} ${addActive.index === index && addActive.show ? `${style.active}` : index === 0 && !addActive.show ? `${style.active}` : ""}`}
onMouseEnter={hoverOn}
onMouseLeave={hoverOff}
key={index}
data-id={index}
>
<Image src={img} alt="offer_images" width={37} height={41} />
<h2>{title}</h2>
<p>{para}</p>
</div>
);
})}
I noticed that "onAdd" property is removed from the updated version of Material-UI, MUI. The only property that is a function is "onDelete" and "onClick". I want to create new chips depending on user input tags. Is there any equivalent way of doing so?
You can use input fields to trigger the creation of new chips. This jsFiddle demo (click "Run" to start the demo) has a button that creates chips.
The important code in that demo relating to your question is below under index.jsx. The key items are:
createNewChip function,
chipName TextField, and
Create New Chip button.
The createNewChip function acts as an event listener for the onClick event of the Create New Chip button. createNewChip takes the text value from the chipName TextField and adds it to the list variable which is managed by React.useState.
list is an array of chip data where each element is an object that looks something like this:
{
id: '11bf5b37-e0b8-42e0-8dcf-dc8c4aefc000',
value: 'A Wonderful Chip',
isValid: true
}
Hopefully, this helps to get you started on a solution.
index.jsx
...
export default function ChipMaker() {
const [ list, setList ] = React.useState( [] );
const selectedItems = list.filter( ( item ) => item.isValid );
const selectedLengthIndex = selectedItems.length - 1;
let listById = {};
for ( let item of list ) {
listById[ item.id ] = item;
}
...
function createNewChip() {
let chipName = document.getElementById( 'chipName' ).value;
let newList = list.concat( {
id: uuidv4(),
value: chipName,
isValid: true
} );
setList( newList );
}
...
return (
<Stack spacing={3} sx={{ width: 500 }}>
<Autocomplete
multiple
id="tags-filled"
filterSelectedOptions={ true }
options={ list.map(( item ) => item.id) }
value={ list.map(( item ) => item.id) }
freeSolo
renderTags={( listIds, getTagProps) =>
listIds.map(( id, index) => (
<Chip
key={ index }
variant="outlined"
label={ listById[ id ].value }
sx={ {
color: ( theme ) => {
let chipColor = '#fff';
if ( typeof( listById[ id ] ) == 'object' ) {
chipColor = listById[ id ].isValid
? theme.palette.common.white
: theme.palette.common.white
}
return chipColor;
},
backgroundColor: ( theme ) => {
let chipColor = '#fff';
if ( typeof( listById[ id ] ) == 'object' ) {
chipColor = listById[ id ].isValid
? theme.palette.primary.main
: theme.palette.error.main
}
return chipColor;
},
[`& .MuiSvgIcon-root.MuiSvgIcon-fontSizeMedium.MuiChip-deleteIcon.MuiChip-deleteIconMedium.MuiChip-deleteIconColorDefault.MuiChip-deleteIconOutlinedColorDefault`]: {
fill: ( theme ) => theme.palette.grey[200]
}
} }
{...getTagProps({ index })}
/>
))
}
renderInput={(params) => (
<TextField
{...params}
variant="filled"
label="Material-UI Chip Input Test"
placeholder="Favorites"
/>
)}
onChange={ validateInput }
/>
<div>
{ selectedItems.map( ( item, index ) => {
let comma = null;
if ( selectedLengthIndex != index ) {
comma = (<span key={ 'idx'+index }>, </span>);
}
return (
item.isValid
? <span key={ index }>{ item.value }{ comma }</span>
: null
);
} ) }
</div>
<TextField
id='chipName'
name='chipName'
className={ 'test' }
type='text'
label={ 'Chip name' }
fullWidth={ false }
variant='standard'
inputProps={ { maxLength: 20 } }
helperText={ 'Enter chip name' }
InputLabelProps={ {
'variant': 'standard',
'color': 'primary',
'disableAnimation': false
} }
FormHelperTextProps={ { 'variant': 'standard' } }
error={ false }
defaultValue={ '' }
placeholder={ 'New chip name' }
color={ 'info' }
/>
<Button
variant={ 'contained' }
onClick={ createNewChip }
sx={ {
width: '200px'
} }
>
{ 'Create new chip' }
</Button>
</Stack>
);
}
/**
* Inject component into DOM
*/
root.render(
<ChipMaker />
);
Actually, I've just come to find out myself that MUI has a component called, "Autocomplete" which is the equivalent of making new chips (and adding tags from user input) with the "ChipInput" component from the older material-UI.
I have this component that processes a card and when you click it redirects you to a past route but your OnClick is not working. I wonder if I could be going wrong
function Characters({
characters,
getAllCharacters,
filterCharacters,
}) {
const history = useHistory();
useEffect(() => {
characters.length === 0 && getAllCharacters();
}, [getAllCharacters]);
return (
<Fragment>
<Header />
<Search characters={characters} />
{ inputCharacters !== "" && filterCharacters.length > 0 ? (
<ListContainer>
{filterCharacters.map((characters) => (
<CardItem
onClick={() => {
history.push(`/characters/${characters.id}`, {
params: { characters },
});
}}
key={characters.id}
name={characters.name}
images={
characters.thumbnail.path + "." + characters.thumbnail.extension
}
/>
)}
</ListContainer>
Component CardItem:
export default function CardItem(props) {
return (
<Container url={props.images}>
<Content>
<p>{props.name}</p>
</Content>
</Container>
);
}
Because you are not using onClick in the CardItem. You just update like this:
<p onClick={props.onClick}>{props.name}</p>
If Container or Content support onClick, you cant put onClick={props.onClick} in this component like a prop
I want to get dynamic content with React js modal I am using package react-responsive-modal. First I render all the post through map. Now I want when I click the individual post the modal should pop up and show me only that particular post's title and body. Now I can't figure out how to get an individual post in modal.
Is it possible to do that via third-party package or I have to make custom modal for that?
import React from 'react';
import Modal from 'react-responsive-modal';
import Axios from 'axios';
const styles = {
fontFamily: 'sans-serif',
textAlign: 'center'
};
class App extends React.Component {
state = {
posts: [],
open: false
};
componentDidMount() {
let url = 'https://jsonplaceholder.typicode.com/posts';
Axios.get(url).then(res => {
this.setState({
posts: res.data.slice(0, 10)
});
console.log(res.data.slice(0, 10));
});
}
onOpenModal = () => {
this.setState({ open: true });
};
onCloseModal = () => {
this.setState({ open: false });
};
renderPosts() {
return this.state.posts.map(post => {
return (
<div
key={post.id}
style={{ width: 400, height: 400, backgroundColor: 'orange' }}
onClick={this.onOpenModal}
>
<h1>{post.title}</h1>
</div>
);
});
}
renderModal(id, title, body) {
return this.state.posts.map(post => {
return (
<div key={post.id} style={{ width: 400, height: 400, backgroundColor: 'orange' }}>
<h1>{post.id}</h1>
<h1>{post.title}</h1>
<p>{post.body}</p>
</div>
);
});
}
render() {
const { open } = this.state;
return (
<div style={styles}>
<h2>react-responsive-modal</h2>
<div>{this.renderPosts()}</div>
<Modal open={open} onClose={this.onCloseModal} center>
<h2>Simple centered modal</h2>
<div>{this.renderModal()}</div>
</Modal>
</div>
);
}
}
export default App;
You'll need to introduce some additional state on your App component that keeps track of the currently selected post. In your onOpenModal() method, you can update that state with the index of the post that was clicked. Then, in renderModal(), you can check what the selected post is and only render that post instead of mapping over the entire array.
class App extends React.Component {
state = {
posts: [],
open: false,
selectedPost: null // Keep track of the selected post
};
componentDidMount() {
let url = "https://jsonplaceholder.typicode.com/posts";
Axios.get(url).then(res => {
this.setState({
posts: res.data.slice(0, 10)
});
console.log(res.data.slice(0, 10));
});
}
onOpenModal = i => {
this.setState({
open: true,
selectedPost: i // When a post is clicked, mark it as selected
});
};
onCloseModal = () => {
this.setState({ open: false });
};
renderPosts = () => {
return this.state.posts.map((post, i) => {
return (
<div
key={post.id}
style={{ width: 400, height: 400, backgroundColor: "orange" }}
onClick={() => this.onOpenModal(i)} // Pass the id of the clicked post
>
<h1>{post.title}</h1>
</div>
);
});
}
renderModal = () => {
// Check to see if there's a selected post. If so, render it.
if (this.state.selectedPost !== null) {
const post = this.state.posts[this.state.selectedPost];
return (
<div
style={{ width: 400, height: 400, backgroundColor: "orange" }}
>
<h1>{post.id}</h1>
<h1>{post.title}</h1>
<p>{post.body}</p>
</div>
);
}
}
render() {
const { open } = this.state;
return (
<div style={styles}>
<h2>react-responsive-modal</h2>
<div>{this.renderPosts()}</div>
<Modal open={open} onClose={this.onCloseModal} center>
<h2>Simple centered modal</h2>
<div>{this.renderModal()}</div>
</Modal>
</div>
);
}
}
In the post onClick function set the post id/index in state along with the open flag
Inside the modal render use the saved index/id to pass that post to the modal as a param/prop.
You dont need to map over all posts inside the modal.
Sample
onOpenModal = (index) => {
this.setState({ open: true, selectedPostIndex: index });
};
onCloseModal = () => {
this.setState({ open: false, selectedPostIndex: undefined })
}
renderPosts() {
return this.state.posts.map((post, index) => {
return (
<div key={post.id} onClick={() => this.onOpenModal(index)}>
<h1>{post.title}</h1>
</div>
)
})
}
render() {
....
<Modal open={open} onClose={this.onCloseModal} center>
<h2>Simple centered modal</h2>
<div>{this.renderModal(this.state.posts[this.state.selectedPostIndex])}</div>
</Modal>
....
}
renderModal(post) {
return (
<div key={post.id} style={{ width: 400, height: 400, backgroundColor: 'orange' }}>
<h1>{post.id}</h1>
<h1>{post.title}</h1>
<p>{post.body}</p>
</div>
)
}
Using React Hooks
Create a modal with dynamic props like this
export default function Modal({
open,
setOpen,
onConfirm,
title,
description,
confirmText,
})
Then render the component.
i did it like this
const getModal = () => {
return (
<Modal open={open} setOpen={setOpen} title={title} description={description} confirmText={confirmText} onConfirm={confirmAction} />
)
}
and then when you want to display your dynamic modal
use a function like this
ConfirmAction can not be a function you should call the function inside that Modal according to that confirmation
const createModal = (title, description, confirmText, confirmAction) => {
setTitle(title);
setDescription(description);
setConfirmText(confirmText);
setConfirmAction(confirmAction);
setOpen(true);
}
Initialize your state with one array which will hold
state = {
posts: [],
open: false,
modalShow: [false,false,false,false,false,false,false,false,false,false] // this is 10 as you have only 10 posts
};
Now modify render posts
onOpenModal = (id) => {
const newModalShow = [...this.state.modalShow];
newModalShow[id] = true;
this.setState({ modalShow: newModalShow});
};
renderPosts() {
return this.state.posts.map((post,index) => {
return (
<Fragement>
<div
key={post.id}
style={{ width: 400, height: 400, backgroundColor: 'orange' }}
onClick={()=>this.onOpenModal(index)}
>
<h1>{post.title}</h1>
</div>
<Modal open={this.state.modalShow[index]} onClose={this.onCloseModal} center>
<h2>post.title</h2>
<div>{this.renderModal()}</div>
</Modal>
<Fragement>
);
});
}
Im fairly new to React and i'm trying to create a dropdown where users can add values to the dropdown. Something like this What i want
This is what i got now, but the add button dosent work at all
My dropdown
I had another input field where i could pass the value to the dropdown, but when i tried to implement the logic to the downshift dropdown nothing happened. No error, no value!
Here is my code:
function BasicAutocomplete({ items, onChange }) {
return (
<Downshift
onChange={onChange}
render={({
getInputProps,
getItemProps,
isOpen,
inputValue,
selectedItem,
highlightedIndex,
handleSubmit
}) => (
<div>
<Input {...getInputProps({ placeholder: 'Markedsaktivitet'}) } ref="input" />
{isOpen ? (
<div style={{ border: '1px solid #ccc' }}>
{items
.filter(
i =>
!inputValue ||
i.toLowerCase().includes(inputValue.toLowerCase()),
)
.map((item, index) => (
<div
{...getItemProps({ item }) }
key={item}
style={{
backgroundColor:
highlightedIndex === index ? 'gray' : 'white',
fontWeight: selectedItem === item ? 'bold' : 'normal',
}}
>
{ item }
</div>
))}
<Button type="button" onClick={handleSubmit}><i className="fa fa-plus" /> Add option</Button>
</div>
) : null}
</div>
)}
/>
)
}
class Dropdown extends React.Component {
constructor(props) {
super(props)
this.state = {
inputField: 'no value',
items: ['apple', 'orange', 'carrot']
}
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit() {
const newItem = this.refs.input.value
this.setState({ items: this.state.items.concat(newItem) })
}
render() {
return (
<Wrapper>
<BasicAutocomplete
items={this.state.items}
onChange={selectedItem => console.log(selectedItem)}
onClick={this.handleSubmit}
/>
</Wrapper>
);
}
}
Thanks for the replays!
use bootstrap dropdown menu its good and nice looking
check out
and maybe you find something
Check here there is diffrent model