I have a component called Component1 in which I have the following code:
import React, { useState } from "react";
import Popover from "material-ui-popup-state/HoverPopover";
import Fab from "#material-ui/core/Fab";
import {
usePopupState,
bindHover,
bindPopover
} from "material-ui-popup-state/hooks";
import PaletteIcon from "#material-ui/icons/Palette";
import Colors from "./Colors";
const DEFAULT_COLOR = "red";
const COLORS = [/*list of colors*/];
const Component1 = ({ classes }) => {
const popupState = usePopupState({
variant: "popover",
popupId: "demoPopover"
});
const [selectedColor, setSelectedColor] = useState(DEFAULT_COLOR);
return (
<div className="box" style={{ backgroundColor: selectedColor }}>
<Fab variant="extended" {...bindHover(popupState)}>
<PaletteIcon />
</Fab>
<Popover
>
<div className="color-palette">
{COLORS.map((color) => (
<Colors
key={color}
selected={selectedColor === color}
onClick={setSelectedColor}
color={color}
/>
))}
</div>
</Popover>
</div>
);
};
export default Component1;
This component is imported in Component2 where the code is:
import React from "react";
import Component1 from "./Component1";
import Fab from "#material-ui/core/Fab";
import DeleteIcon from "#material-ui/icons/Delete";
function Component2(props) {
function handleClick() {
props.onDelete(props.id);
}
return (
<div className="note" style={{ backgroundColor: "selectedColor" }}>
<h1>{props.title}</h1>
<p>{props.content}</p>
<Fab onClick={handleClick}>
<DeleteIcon fontSize="small" />
</Fab>
<HoverPopover />
</div>
);
}
export default Component2;
In component2 I need to use the const selectedColor for styling purpose for div with class="note". However the issue is when I select colors from COLORS list the background-color of div with class="note" is not changing. I tried many options but I don't understand how to do it correctly. Please tell me how to do it right.
To share the "selectedColor" variable, which is actually a state, you would have to pass it through the props to the child component
Your "Component2" should declare the state "selectedColor", and this state and its function must be passed by the props to your "Component1".
https://reactjs.org/tutorial/tutorial.html#lifting-state-up
Related
I need some ideas for a folder structure/mental model in React.
I have an Item component. It stays the same.
It can be wrapped in either a Link with a URL prop or a Button with an onClick prop.
My current folder structure solution looks like this:
-Item
-wrapperComponents
-Link
-Button
Both the Link and Button components wrap around the children prop. Much like this:
react stuff ...
return(
<button onClick={props.handleOnClick}>
{props.children}
</button>
)
And this is how I call them:
<Button>
<Item />
</Button>
or
<Link>
<Item />
</Link>
I am looking for a better, more elegant solution.
I've tried sending the wrapper components to the Item but React doesn't allow to use them as a wrapper that would take children.
This is what you can do:
import "./styles.css";
import Item from "./Item";
import Wrapper1 from "./Wrapper1";
export default function App() {
return (
<div className="App">
<Item wrapper={Wrapper1} />
</div>
);
}
Item component:
const Item = ({ wrapper: Wrapper }) => {
return (
<Wrapper>
<div>This is my child</div>
</Wrapper>
);
};
export default Item;
Wrapper component:
const Wrapper1 = ({ children }) => {
return (
<div>
This is my wrapper
{children}
</div>
);
};
export default Wrapper1;
https://codesandbox.io/s/quirky-vaughan-4op2l1?file=/src/index.js
I've come up with this solution with the help you you guys
https://codesandbox.io/s/create-react-app-forked-wlmkyv?file=/src/App.js
Main component App.js:
import * as React from "react";
import Wrapper from "./components/Wrapper";
import Item from "./components/Item";
function App() {
return (
<div>
<Item wrapper={Wrapper} wrapperProps={{ color: "orange" }} />
</div>
);
}
export default App;
Item component Item.js:
import * as React from "react";
function Item(props) {
const Wrapper = props.wrapper;
const wrapperProps = props.wrapperProps;
return (
<Wrapper {...wrapperProps}>
<div>An Item</div>
</Wrapper>
);
}
export default Item;
Wrapper component Wrapper.js:
import * as React from "react";
function Wrapper(props) {
return (
<div style={{ backgroundColor: props.color || "red" }}>
{props.children}
</div>
);
}
export default Wrapper;
I am trying to use matrial ui elements,but my issue is that it is not responsive. I want to show 2
items on medium screen and just 1 on small ones, but it is 3 on all screens. thx
this is the parent component and I prop drill the array's elements to the child component
import * as React from "react";
import Box from "#mui/material/Box";
import ImageList from "#mui/material/ImageList";
import ImageListItem from "#mui/material/ImageListItem";
import ImageListItemBar from "#mui/material/ImageListItemBar";
import InfiniteScroll from "react-infinite-scroll-component";
import Grid from "#mui/material/Grid";
import ProductListItem from "./ProductListItem";
const ProductList = ({ products }) => {
return (
<>
<div className="mr-5 ml-5">
<Box>
<ImageList variant="masonry" cols={3} gap={18}>
{products.map((item) => (
<ProductListItem
item={item}
key={item.id}
/>
))}
</ImageList>
</Box>
</div>
</>
);
};
export default ProductList;
this is the child component for each element of array
import React, { useState, useEffect } from "react";
import Heart from "react-heart";
import { useLocalStorage } from "./storage.js";
import ImageListItem from "#mui/material/ImageListItem";
import ImageListItemBar from "#mui/material/ImageListItemBar";
import ImageList from "#mui/material/ImageList";
const ProductListItem = (props) => {
return (
<>
<ImageListItem>
<img
src={`${props.item.img_src}?w=248&fit=crop&auto=format`}
srcSet={`${props.item.img_src}?w=248&fit=crop&auto=format&dpr=2 2x`}
alt={props.item.title}
loading="lazy"
/>
<div style={{ width: "2rem" }} className="ml-2 mt-2">
<Heart />
</div>
<ImageListItemBar
subtitle={<span>by: aaaaaaaaaaaa</span>}
position="below"
align="right"
/>
</ImageListItem>
</>
);
};
export default ProductListItem;
Hi I mimic a project and there is no problem but can not display the ui,I am not sure what kind of problem it is.The object project link:https://codesandbox.io/s/learning-react-color-organizer-2-iytxb?file=/src/StarRating.js
My project link:https://codesandbox.io/s/nervous-flower-xv669?file=/src/App.js
My project has 2 warnings,this first warning is Each child in a list should have a unique "key" prop. which direct to the app.js:
import React, { useState } from "react";
import Data from "./color-data.json";
import RatingList from "./RatingList";
export default function App() {
const [colors,setColors]=useState(Data)
const removeId=id=>{
const newcolors=colors.filter(color=>color.id !== id);
setColors(newcolors);
}
return (
<div>
{
<RatingList
colors={colors}
onDelete={removeId}
/>
}
</div>
);
}
the second warning is Cannot update a component (App) while rendering a different component (ColorRating).which direct to the ColorRating.js:
import React from "react";
import Star from "./Star";
import { AiFillDelete } from "react-icons/ai";
export default function ColorRating({id,
title,
color,
rating,
onDelete = (f) => f,
}) {
return (
<div>
<h1>{title}</h1>
<h1>
<AiFillDelete onClick={onDelete(id)} />
</h1>
<h1 style={{ backgroundColor: color, padding: "30px" }}> </h1>
{[...Array(5)].map((n, i) => (
<Star key={i} selected={rating > i} />
))}
<p>{rating} of 5 stars</p>
</div>
);
}
No, I mean remove the { } curly brackets from the return block.
And also, change this line:
<AiFillDelete onClick={onDelete(id)} />
to this (right now you invoke it's on every rerender):
<AiFillDelete onClick={() => onDelete(id)} />
I have an Add.js that contains the main Form. I have a CheckboxesTags.js that is a function component.
I need to get selected items of CheckboxesTags.js in Add.js to send by axios with other values.
I added <CheckboxesTags onChange={(e)=>{console.log(e.currentTarget);}} /> but I cannot get values.
Add.js:
import React, { Component } from 'react';
import CheckboxesTags from './CheckboxesTags'
class Add extends Component {
constructor() {
super();
}
render() {
return (
<div >
<CheckboxesTags onChange={(e)=>{console.log(this.props);}} />
</div>
)
}
}
export default Add;
and the CheckboxesTags.js is exactly material-ui code:
import React from 'react';
import axios from 'axios'
import Checkbox from '#material-ui/core/Checkbox';
import TextField from '#material-ui/core/TextField';
import Autocomplete from '#material-ui/lab/Autocomplete';
import CheckBoxOutlineBlankIcon from '#material-ui/icons/CheckBoxOutlineBlank';
import CheckBoxIcon from '#material-ui/icons/CheckBox';
const icon = <CheckBoxOutlineBlankIcon fontSize="small" />;
const checkedIcon = <CheckBoxIcon fontSize="small" />;
export default function CheckboxesTags() {
return (
<Autocomplete
multiple
id="checkboxes-tags-demo"
onChange={(e)=>{console.log(e.currentTarget);}}
options={optins}
disableCloseOnSelect
getOptionLabel={option => option.name}
renderOption={(option, { selected }) => (
<React.Fragment>
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
style={{ marginRight: 8 }}
checked={selected}
/>
{option.name}
</React.Fragment>
)}
style={{ width: 500 }}
renderInput={params => (
<TextField {...params} variant="outlined" label="Options" placeholder="Favorites" />
)}
/>
);
}
let optins = [
];
axios.get('http://localhost:5000/api/products/notes').then(response => {
optins = response.data
})
You might need to test format of response.data with console.log(response.data) cause options in Autocomplete is array.If it is return objects of array you might need to use Object.keys()(to get the data object to an array) to get the optins array.
Essentially you need to pass the click handler down from the parent component. Something like this (Not tested):
import React, { Component } from 'react';
import CheckboxesTags from './CheckboxesTags';
class Add extends Component {
constructor(props) {
super(props);
}
handleTagClick = tag => console.log(tag);
render() {
return (
<div>
<CheckboxesTags onSelect={this.handleTagClick} />
</div>
);
}
}
export default Add;
import React from 'react';
import axios from 'axios';
import Checkbox from '#material-ui/core/Checkbox';
import TextField from '#material-ui/core/TextField';
import Autocomplete from '#material-ui/lab/Autocomplete';
import CheckBoxOutlineBlankIcon from '#material-ui/icons/CheckBoxOutlineBlank';
import CheckBoxIcon from '#material-ui/icons/CheckBox';
const icon = <CheckBoxOutlineBlankIcon fontSize="small" />;
const checkedIcon = <CheckBoxIcon fontSize="small" />;
export default function CheckboxesTags(props) {
const { onSelect } = props;
return (
<Autocomplete
multiple
id="checkboxes-tags-demo"
onChange={(event, value) => {
onSelect(value);
}}
options={optins}
disableCloseOnSelect
getOptionLabel={option => option.name}
renderOption={(option, { selected }) => (
<React.Fragment>
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
style={{ marginRight: 8 }}
checked={selected}
/>
{option.name}
</React.Fragment>
)}
style={{ width: 500 }}
renderInput={params => (
<TextField
{...params}
variant="outlined"
label="Options"
placeholder="Favorites"
/>
)}
/>
);
}
Since you already have the onChange event attached to the Autocomplete element, this will fire any code contained within it. In this case it would be the passed down onSelect function.
I have a <Button /> component and an <Icon/> component.
I try to implement a button with an icon.
The Button.jsx story:
import React from "react";
import { storiesOf } from "#storybook/react";
import Button from "../components/Button";
import Icon from "../components/Icon/Index";
import { iconTypes } from "../components/Icon/Index";
storiesOf("Button/Primary", module)
.add("With icon", () => (
<Button><Icon type={iconTypes.arrowRight}/></Button>
))
That works fine but I would like the api for a Button with an icon to be-
<Button icon={icons.arrow}>Click Me</Button>
How can I achieve that?
The Icon.jsx story:
import React from "react";
import { storiesOf } from "#storybook/react";
import Icon from "../components/Icon/Index";
import { iconTypes } from "../components/Icon/Index";
storiesOf("Icon", module)
.add("Arrow Right", () => (
<Icon type={iconTypes.arrowRight}>
</Icon>
))
.add("Arrow Left", () => (
<Icon type={iconTypes.arrowLeft}>
</Icon>
));
The <Button /> component:
import React from 'react';
import { css, cx } from 'emotion';
import colors from '../styles/colors';
export default function Button({
children,
...props
}) {
const mergedStyles = cx(
// css
);
// other css stuff
return (
<button {...props} disabled={disabled} className={mergedStyles}>
{children}
</button>
);
And <Icon /> component:
import React from "react";
import { css } from 'emotion';
import ArrowRight from "./arrow-right2.svg";
import ArrowLeft from "./arrow-left2.svg";
export const iconTypes = {
arrowRight: 'ARROW_RIGHT',
arrowLeft: 'ARROW_LEFT',
}
const iconSrc = {
ARROW_RIGHT: ArrowRight,
ARROW_LEFT: ArrowLeft,
}
const circleStyles = css({
width: 24,
height: 24,
borderRadius: "50%",
backgroundColor: "#f7f7f9",
display: "flex",
justifyContent: "center"
});
export default function Icon({ type }) {
return (
<div className={circleStyles}>
<img src={iconSrc[type]} />
</div>
)
};
Any help would be appreciated.
import React from 'react';
import {css, cx} from 'emotion';
import colors from '../styles/colors';
//import your ICON component & make sure your path is right
import Icon from "./../Icon";
export default function Button({
children,
disabled,
icon,
...props
}) {
const mergedStyles = cx(//your css);
return (
<button {...props} disabled={disabled} className={mergedStyles}>
// If icon prop is provided then render ICON component
{icon && <Icon type={icon}/>}
//Other children
{children}
</button>
);
}
in render of Button, you can do something like that :
Button.js:
render(){
const { icon } = this.props
return(
<Button>
{icon && <Icon type={icon}/>}
<Button>
)
}