I'm fairly new to React and experimenting with React. I am making a set of cubes that when a user clicks, alternate between colors.
To learn more about setState, I added an input so that when the user enters a color, the color of the cubes changes. The problem is the color of the cube changes, but when I click on the cube it goes back to the default one. I want the cubes to change the colors that are inputted by the user.
What have I tried?
I tried changing pink in this.setState({color: 'pink'}) to setColor(), but it doesn't work. I looked around here, but couldn't find anything that would answer my question.
I've created the issue here.
class TicTacToe extends Component {
state = {
color: 'black',
colors: 'white',
count: 0
}
changeColor(c){
this.setState({ count: this.state.count + 1 })
if(this.state.count % 2 === 0){
this.setState({color: 'pink'})
this.setState({colors: 'grey'})
} else if (this.state.count % 2 !== 0){
this.setState({color: 'grey'})
this.setState({colors: 'pink'})
}
}
setColor(color){
return this.setState({color: color})
}
setColors(color){
this.setState({colors: color})
}
render(){
return (
<div className="main">
<div className="inputFields">
<span> Color One:
<input value={this.state.color}
onChange={(e)=> this.setColor(e.target.value)} /> </span>
<br /><br />
<span> Color Two:
<input value={this.state.colors}
onChange={(e)=> this.setColors(e.target.value)} /> </span>
</div>
<div onClick= {(c)=> this.changeColor()}>
<div className='square' style={{backgroundColor: this.state.color}}></div>
<div className='square' style={{backgroundColor: this.state.colors}}></div>
<div className='square' style={{backgroundColor: this.state.color}}></div>
<br />
<div className='square' style={{backgroundColor: this.state.colors}}></div>
<div className='square' style={{backgroundColor: this.state.color}}></div>
<div className='square' style={{backgroundColor: this.state.colors}}></div>
<br />
<div className='square' style={{backgroundColor: this.state.color}}></div>
<div className='square' style={{backgroundColor: this.state.colors}}></div>
<div className='square' style={{backgroundColor: this.state.color}}></div>
</div>
<span> This is the count: {this.state.count} </span>
</div>
)
}
}
export default TicTacToe;
It goes back to the default onClick, because onClick you are manually setting the colors, Also you don't need a count variable to alternate between colors. The count state is also not used properly. All you need is to use functional setState and implement changeColor like
changeColor(c){
this.setState(prevState => ({
color: prevState.colors,
colors: prevState.color
}))
}
Working Demo
Check When to use functional setState
Check setState doesn't update the state immediately
Related
I have a button to show details of each card in my react project like this. the amount of cards are unknown and it is based on what I get from API.
just like this, I get the data and I'm mapping it:
<div className={styles.contentCardContainer}>
{factorList !== undefined && factorList.length > 0 ? (
factorList.map((list, i) => (
<div key={uuidv4()} className={styles.contentCard}>
<IconButton
color={showListService ? "error" : "success"}
onClick={() => {
setShowlistService((prev) => !prev);
HandleFetchDetails(list.ID);
}}
sx={{
display: "flex",
alignItems: "center",
alignSelf: "flex-start",
}}
>
{showListService ? <RemoveCircle /> : <AddCircle />}
</IconButton>
<section className={styles.eachSection}>
<img src={doctorImage} alt="doctor" />
<p>{list.Doctor}</p>
</section>
<section className={styles.eachSection}>
<img src={calendarImage} alt="doctor" />
<p>
{list.Date}
{list.Time}
</p>
</section>
<section className={styles.eachSection}>
<img src={RialImage} alt="doctor" />
<p>{list.Payable} ریال</p>
</section>
<section className={styles.eachSection}>
<img src={discountImage} alt="doctor" />
<p>{list.discount}</p>
</section>
{showListService && (
<DetailsListService listDetails={listDetails} styles={styles} />
)}
</div>
))
) : (
<Alert
variant="filled"
severity="warning"
sx={{ justifyContent: "space-between" }}
>
<p>There's no data</p>
</Alert>
)}
</div>
</div>
as you can see, I have IconButton element that I mapped it so each element have this button. but there's a problem.
I just have one state.
just like this :
const [showListService, setShowlistService] = React.useState(false);
since the amount cards I will receive is unknown. I can't define multiple states, because I don't know how many I should define. this is a bad practice too.
so whenever I click on IconButton, state changes for ALL buttons. how can I make it work individually.
change state for just a button in that specific element
Create a component for the cards and each card can keep the state:
factorList.map((factor, index) => {
return <Card key={index} factor={factor} />;
});
const Card = ({ factor }) => {
const [state, setState] = useState(false);
return <div>Your card stuff here</div>;
};
I work with buttons, and on click i need to change color button.
I have two button: favorite and stats, and onclick i need to change to custom color, for ex favorite is orange, stats blue.
How i can change color onclick?
<div className={classes.fotter_button}>
<button>
<FontAwesomeIcon
icon={faStar}
aria-hidden="true"
/>
</button>
<button>
<FontAwesomeIcon
icon={faChartBar}
aria-hidden="true"
/>
</button>
</div>
const [favorite,setFavorite]=useState(false)
const [stats,setStats]=useState(false)
<div className={classes.fotter_button}>
<button onClick={()=>{setFavorite(true)}} style={{backgroundColor:favorite==true?"orange":""}}>
<FontAwesomeIcon
icon={faStar}
aria-hidden="true"
/>
</button>
<button onClick={()=>{setStats(true)}} style={{backgroundColor:stats==true?"blue":""}}>
<FontAwesomeIcon
icon={faChartBar}
aria-hidden="true"
/>
</button>
</div>
One way is to add state variable in your component, and use a function to change the state variable between two values (true, false). Apply the button styling based on the value of the state variable.
const MyToggleButton = () => {
const [toggle, setToggle] = React.useState(false);
const toggleButton = () => setToggle(!toggle);
return (
<>
<button style={{backgroundColor: toggle ? '#FFF' : '#000'}} onClick={toggleButton}>Click Me</button>
</>
);
}
My example is pretty generic; if possible consider using a state variable that more aptly describes your buttons two states.
I have this toolbar with 2 custom buttons and 3 imported material data grid toolbar components, I want them to match but preferably apply the material styles to my custom components yet I can't find a style import for them anywhere in their docs. But anyway I can get them to match would be huge. Does anyone have some advice?
const Toolbar = () => {
return (
<GridToolbarContainer>
<div
className={classes.toolbar}
onClick={() => {
toggleSearch();
}}
>
<SearchIcon />
<p>SEARCH</p>
</div>
<div
className={classes.toolbar}
onClick={() => {
bookmarkCases(selectedRows);
}}
>
<BookmarkBorderIcon />
<p>SAVE CASES</p>
</div>
<GridColumnsToolbarButton />
<GridDensitySelector />
<GridToolbarExport />
</GridToolbarContainer>
);
};
Instead of making an icon button out of a <div>, why not use the startIcon prop in an MUI <Button />, e.g.
From this:
<div
className={classes.toolbar}
onClick={() => {toggleSearch()}}
>
<SearchIcon />
<p>SEARCH</p>
</div>
to this:
<Button
className={classes.toolbar}
onClick={() => {toggleSearch()}}
startIcon={<SearchIcon />}
>Search
</Button>
All the styles should match because MUI DataGrid toolbar buttons are just MUI Buttons
After a bit of a struggle I found a solution. I was able to achieve a consistent style for all toolbar buttons by passing the styles through the DataGrid itself.
export const StyledDataDrid = styled(DataGridPremium)<DataGridPremiumProps>(({ theme }) => ({
'& .MuiDataGrid-toolbarContainer': {
'& .MuiButton-text': {
fontSize: '16px !important',
color: '#074682',
},
'& .MuiBadge-badge': {
backgroundColor: '#074682',
},
},
}));
Or you can style it on the DataGrid component using the sx property as explained in this issue: https://github.com/mui/mui-x/issues/3686#issuecomment-1018469717
I am implementing a multiple image upload. The user should have the possibility to select the images and see a small preview of the selected images. I store the image URL in an array and iterate the url to display the images. The problem is that the image is not updated in the preview. I.e. the first, second, etc. Image is the same (see attached picture). When I look at the image URL as text, it becomes clear that the links are different and lead to different images (see picture). So I don't understand why the same image is displayed overall iterations although the url is different.
export class ImageUploadView extends Component {
constructor(props) {
super(props);
this.onChangeImage = this.onChangeImage.bind(this);
this.fileObj = [];
this.fileArray = [];
}
onChangeImage (e) {
this.fileObj.push(e.target.files)
for (let i = 0; i < this.fileObj[0].length; i++) {
this.fileArray.push(URL.createObjectURL(this.fileObj[0][i]))
}
}
render() {
return (
<React.Fragment>
<div className="card card-body mt-4 mb-4">
<h1>Add Image</h1>
<div>
<input type="file" onChange={this.onChangeImage} multiple ref={this.inputRef} />
{(this.fileArray || []).map((url) => (
<MDBRow key={url} style={{ margin: '10px', display: 'inline-block' }}>
<MDBCol>
<MDBCard style={{ width: '5rem' }}>
<MDBCardImage className="img-fluid" src={url} waves />
<MDBCardBody>
<p> {url} </p>
</MDBCardBody>
</MDBCard>
</MDBCol>
</MDBRow>
))}
</div>
</div>
</React.Fragment>
);
}
}
I have a created a stacked and grouped bar chart with react + d3 and i want to add legend, i have added it like:
{allKeys.map((key) => (
<div key={key} className="field" style={{ display: "flex" }}>
<input
id={key}
type="checkbox"
checked={keys.includes(key)}
onChange={(e) => {
if (e.target.checked) {
setKeys(Array.from(new Set([...keys, key])));
} else {
setKeys(keys.filter((_key) => _key !== key));
}
}}
/>
<label htmlFor={key} style={{ color: colors[key] }}>
{key}
</label>
</div>
))}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
but the problem is that when you toggle (for example clicking on facebook to toggle) it will change stack order (for example facebook color is green and it should be always at the bottom, but when you click on facebook legend to make it disappear and click again to appear first it is at the bottom and when you hide and show back it will go to the top)
you can check the full code and demo in demo
any help to make it work will be appreciated
Two possible solutions:
Use an object instead of an array, the key being the label and the value being whether it's toggled or not:
const initialItems = {
facebook: true,
google: true,
linkedin: true
}
{Object.keys(items).map((key) => (
<div key={key} className="field" style={{ display: "flex" }}>
<input
id={key}
type="checkbox"
checked={items[key]}
onChange={(e) => {
if (e.target.checked) {
items[key] = true;
} else {
items[key] = false;
}
setItems(items);
}}
/>
<label htmlFor={key} style={{ color: colors[key] }}>
{key}
</label>
</div>
))}
Change the set function to use allKeys as a base:
if (e.target.checked) {
setKeys(allKeys.filter(k => keys.includes(k) || k === key));
}