How to change the state variable as true based on index value - javascript

when i clicked on close button the val will immedietly changes to Fruit. how to do that?
for example
Apple close
Grapes close
Pineapples close
otherwise how can we set the state cancel is true based on the index.
I mean if i clicked on Apple close button, only that index value will true other are false. can we do this?
here is the sample code
constructor(){
super();
this.state={
cancel: false;
}
}
<table>
<tr>
{
products.map((val, index)=>{
<td>{val} <button>closeIcon</button></td>
});
}
</tr>
</table>
if anyone help me how to solve this problem
Here i have asked two questions.
Outof these you can answer one question.

I believe this is what you're looking for, only one fruit can be closed
export default class Test extends React.Component {
constructor() {
super();
this.state = {
cancel: false,
products: [
{
name: "Apple",
closed: false
},
{
name: "Grapes",
closed: false
},
{
name: "Pineapples",
closed: false
}
]
};
this.onClick = this.onClick.bind(this);
}
onClick(e) {
this.setState((s) => ({
...s,
products: s.products.map((p) => ({
...p,
closed: p.name === e.target.name
}))
}));
}
render() {
return (
<table>
<tr>
{this.state.products.map((val, index) => {
return (
<td>
{val.closed ? "CLOSED" : "OPEN"} {val.name} {" "}
<button name={val.name} onClick={this.onClick}>
closeIcon
</button>
</td>
);
})}
</tr>
</table>
);
}
}
Take a look https://codesandbox.io/s/silly-swirles-wfnks?file=/src/App.js
To make all can close:
onClick(e) {
this.setState((s) => ({
...s,
products: s.products.map((p) =>
p.name === e.target.name ? { ...p, closed: true } : p
)
}));
}
https://codesandbox.io/s/tender-glade-6vg6b

You need to have a seperate state for Button component in that is being rendered by map. In this way you'll be able to setState for every individual Button component.
Wrap your button in a seperate component
export default class ButtonComponent extends React.Component{
constructor(){
super();
this.state={
cancel: false;
}
}
onCancel = () => {
this.setState({...this.state, cancel: true})
}
render(){
return(
<td>{val} <button onClick={this.onCancel}>closeIcon</button>
</td>
)
}
}
render(){
return(
<table>
<tr>
{
products.map((val, index)=>{
<ButtonComponent key={index} />
});
}
</tr>
</table>
)
}

You are not returning the anything from your map function. After fixing it, simply updating the state with this.setState on onClick would do
<tr>
{
products.map((val, index)=>{
return (<td>{val} <button>closeIcon</button></td>);
});
}
</tr>
See the jsfiddle here, https://jsfiddle.net/qzmu5307/
Edit: If you just want to remove an item from the list on close button, you can simply remove that item from the array in the state (for now i am doing so by searching the value in the array, but this is usually a bad practice. Assign them some identifiers for this uprose).

Related

How to toggle watch button to watched and vice versa for each list item

I created a state for isWatched where if isWatched === true, then I want the button to say "Watched" and if it's false, then the button says "Watch" I want to be able to toggle on each button and conditionally render the name of button upon click. However, though the state is changing upon click, the name of the button is not. I believe it's because the button needs to be defined in the return statement to be able to update but I defined all my logic upon a setState condition in the handleSearch method so I'm not sure how to fix this without having to refactor everything. I also have an issue where I need to isolate each button because if I change the state, all the buttons get changed.
import React from 'react';
import movies from './movieData';
const movieTitles = movies.map(movie => movie.title);
class SearchBar extends React.Component {
constructor(props) {
super(props);
this.state = {
searchQuery: '',
searchedMovies: [],
isMovieFound: null,
isWatched: false
}
}
handleQuery = e => {
this.setState({
searchQuery: e.target.value
});
}
handleWatched = () => {
this.setState(prevState => ({
isWatched: !prevState.isWatched
}));
}
handleSearch = () => {
if (movieTitles.filter(movie => movie.toLowerCase()
.includes(this.state.searchQuery.toLowerCase())).length === 0) {
this.setState({ isMovieFound: false, searchedMovies: [], searchQuery: '' });
} else {
this.setState({
searchedMovies: movieTitles.filter(movie =>
movie.toLowerCase().includes(this.state.searchQuery.toLowerCase()))
.map(movie => <li key={movie}>{movie}<button className="watchBtn" onClick={this.handleWatched}>{this.state.isWatched ? 'Watched' : 'Watch'}</button></li>),
isMovieFound: true,
searchQuery: ''
});
}
}
render() {
return (
<div>
<input
type="text"
placeholder="Search for a movie.."
name="searchQuery"
value={this.state.searchQuery}
onChange={this.handleQuery}
/>
<button
onClick={this.handleSearch}
>
Search
</button>
<br />
<ul>
{this.state.searchedMovies}
</ul>
{this.state.isMovieFound === false && <span>No Movie Found</span>}
</div>
);
}
}
export default SearchBar;
Yes. You are right. You have rendered searchedMovies on click of Search button. I dont understand why do u store UI elements in state. Thats a bad practice. Try to just store the filtered movies in your store and write rendering logic in render method itself like below:
handleSearch = () => {
if (movieTitles.filter(movie => movie.toLowerCase()
.includes(this.state.searchQuery.toLowerCase())).length === 0) {
this.setState({ isMovieFound: false, searchedMovies: [], searchQuery: '' });
} else {
this.setState({
searchedMovies: movieTitles.filter(movie => movie.toLowerCase().includes(this.state.searchQuery.toLowerCase())),
isMovieFound: true,
searchQuery: ''
});
}
and add below code in render function
<ul>
{this.state.searchedMovies.map(movie => <li key={movie}>{movie}<button className="watchBtn" onClick={this.handleWatched}>{this.state.isWatched ? 'Watched' : 'Watch'}</button></li>)}
</ul>

How to invoke button click in List in Reactjs

I want to have 3 buttons and 3 related components . So that when button click show its related component . Here is my code :
Main Component :
class Main extends Component { constructor(props) {
super(props);
this.state = {
show: false,
}}
onClick(e) {
e.preventDefault()
console.log(e.target.id)
this.setState({ show: !this.state.show }); }
renderTabs() {
var child = this.props.child;
var items = [];
__.forEach(child, (item, index) => {
items.push(
<div>
<Button id={index} onClick={(e) => this.onClick(e)}>{item.data}</Button>
{this.state.show ? (
<CropComponent title={item.data} opts={item.opts} data={item.child} />
) : null}
</div>
);
});
return items;
}
render() {
var opts = this.props.opts;
return (
<div>
{this.renderTabs()}
</div>
)}
}
export default Main;
And here is my CropComponent :
class CropComponent extends Component {
constructor(props) {
super(props);
this.state = {
};
}
render() {
return (
<div>hello {this.props.data}</div>
);
}
}
export default CropComponent;
Here are my buttons how can i show its related component when click on a button and hide it on re-click the same button ?
Maintain a show state with initial value as -1. Supply event and index to onClick. In onClick do setState of show as index.
Check if show === index and render the corresponding component.
Like this
this.state = {
show: -1
}
onClick(e,index) {
e.preventDefault()
this.setState({ show: show===index? -1 : index});
}
__.forEach(child, (item, index) => {
items.push(
<div>
<Button id={index} onClick={(e) => this.onClick(e,index)}>{item.data}</Button>
{this.state.show === index ? (
<CropComponent title={item.data} opts={item.opts} data={item.child} />
) : null}
</div>
);
});
Edit:
updated the answer based on helpful comment by #Tony Nguyen

dynamically generated Toggle (switches) in react js not working

There are two types of switch status in my project. One is default and the other is generated from API.When the item is changed toggle switch on/off won't work.
constructor(props) {
super(props);
this.state = {
switch_status: [true, false],
items: [{title:toyota}, {title:bmw}]
}
}
There is a function, Which get data from API and set into items:
changeItems = () => {
this.setState({ items: [{title:toyota, switch_status: true},
{title:porche, switch_status: true},
{title:bmw, switch_status: false}]
});
}
on/off not working, When Items changed:
//Switch on/off function
handleChange = (event, id) => {
const isChecked = event;
this.setState(
({switch_status}) => ({
switch_status: {
...switch_status,
[id]: isChecked,
}
})
);
}
//Loop Items
this.state.items.map((item, index) => (
<Switch
className="custom-switch custom-switch-primary"
checked={this.state.switch_status[index]}
id={index}
onChange={event => handleChange(event, index)}
/>
))
There is nothing wrong in your state handling logic really but your componentDidUpdate() is getting called infinite times because the check inside is not working and it overwrites your toggle state even when you don't need to.
Change you componentDidUpdate() to:
componentDidUpdate(previousProps, previousState) {
if (
JSON.stringify(previousProps.mediaTypes.items) !==
JSON.stringify(this.props.mediaTypes.items)
) {
this.dataListRender();
this.setState({
customMediaTypesItems: this.props.mediaTypes.items.custom_media_types
});
}
}
First of all; you are passing a new reference to a component as prop on every render and that causes needless DOM updates
Second is that you initialise the state with a different structure than when you are setting state. I assume that
{
items: [
{ title: toyota, switch_status: true },
{ title: porche, switch_status: true },
{ title: bmw, switch_status: false }
];
}
Is your actual state because you use that to render. You can do the following:
const Switch = React.memo(
//use React.memo to create pure component
function Switch({ label, checked, toggle, id }) {
console.log("rendering:", label);
// prop={new reference} is not a problem here
// this won't re render if props didn't
// change because it's a pure component
// if any of the props change then this needs to re render
return (
<label>
{label}
<input
type="checkbox"
checked={checked}
onChange={() => toggle(id)}
/>
</label>
);
}
);
class App extends React.PureComponent {
state = {
items: [
{ title: "toyota", switch_status: true },
{ title: "porche", switch_status: true },
{ title: "bmw", switch_status: false }
]
};
toggle = _index =>
this.setState({
items: this.state.items.map((item, index) =>
_index === index // only toggle the item at this index
? { ...item, switch_status: !item.switch_status }
: item // do not change the item
)
});
render() {
//please note that using index is not a good idea if you
// change the order of the state.items, add or remove some item(s)
// if you plan to do that then give each item a unique id
return (
<div>
{this.state.items.map((item, index) => (
<Switch
label={item.title}
checked={item.switch_status}
toggle={this.toggle}
id={index}
key={index}
/>
))}
</div>
);
}
}
//render app
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
I believe there is an issue while getting the checked state.
In your current implementation, you have written const isChecked = event; in the handleChange method which will always be true since the event object is always available.
It should be const isChecked = event.target.checked; for it to set the toggled checkbox state correctly.

Sharing state between React components

I have a table component (parent) and in each row of the table, there's another component that's basically an image button (child). When clicked, the image button switches from its default three vertical dots (https://png.icons8.com/windows/1600/menu-2.png) to a download icon. The three vertical dots image has an onClick listener that switches it to the download icon and the download icon has an onClick listener that downloads the file in the table row. Each image button has it's own state (based on which image to display). In the table component, I have a div that wraps the entire screen. When that div is clicked (so basically if you click anywhere outside the images), I want to be able to reset all the images in the table back to the three dots. I'm not sure how to accomplish this since there's many child components, each with it's own state (therefore I don't think redux would work). Is there a way to share the state of each image button with the table component?
Here's some of the table code
<div className='shield' onClick={() => this.resetDownloads()}></div>
<table className='table table-striped'>
<tbody>
this.props.songs.data.map((song, index) =>
<tr key={index}>
<td>{song.name}</td>
<td>{song.artist}</td>
<td>{song.duration}</td>
<td><ActionButton song={song}/></td>
</tr>
</tbody>
</table>
Here's some of the ActionButton component
class ActionButton extends React.Component {
constructor(props) {
super(props);
this.state = {
download: false
};
this.toggle = this.toggle.bind(this);
}
toggle(e) {
e.stopPropagation();
this.setState({
download: !this.state.download
});
}
render() {
return this.state.download ? (
<div>
<img src={Download} width='15%' onClick={(e) => this.props.downloadSong(this.props.song, e)}></img>
</div>
)
: (
<div>
<img src={Dots} width='15%' onClick={(e) => this.toggle(e)} className='image'></img>
</div>
)
}
}
This is where you lift your state up. Actually, in the first place, you don't need a state in your ActionButton component. It should be a stateless component. You can keep all your data in the parent component.
Let's assume there is an id property in the song data. You can track a downloadState in the parent component and add this song's id to this state object. Then you can pass this value to your ActionComponent and use it. Also, you can keep all your functions in your parent component.
const songs = [
{ id: "1", name: "Always Blue", artist: "Chet Baker", duration: "07:33" },
{ id: "2", name: "Feeling Good", artist: "Nina Simone", duration: "02:58" },
{ id: "3", name: "So What", artist: "Miles Davis", duration: "09:23" },
]
class App extends React.Component {
state = {
downloadState: {},
}
toggle = ( e, id ) => {
e.stopPropagation();
this.setState( prevState => ({
downloadState: { ...prevState.downloadState, [id]: !prevState.downloadState[id]}
}))
}
downloadSong = ( e, song ) => {
e.stopPropagation();
alert( song.name );
}
resetDownloads = () => this.setState({ downloadState: {}});
render() {
return (
<div onClick={this.resetDownloads}>
<table>
<tbody>
{
songs.map((song, index) => (
<tr key={index}>
<td>{song.name}</td>
<td>{song.artist}</td>
<td>{song.duration}</td>
<td>
<ActionButton
toggle={this.toggle}
song={song}
downloadState={this.state.downloadState}
downloadSong={this.downloadSong}
/>
</td>
</tr>
))
}
</tbody>
</table>
</div>
)
}
}
const ActionButton = props => {
const { downloadState, downloadSong, song, toggle } = props;
const handleToggle = e => toggle(e, song.id);
const handleDownload = e => downloadSong( e, song );
const renderImages = () => {
let images = "";
if ( downloadState[song.id] ) {
images = <p onClick={handleDownload}>Download</p>;
} else {
images = <p onClick={handleToggle}>Dots</p>;
}
return images;
}
return (
<div>{renderImages()}</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
table, th, td {
border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
If there isn't any id property then you can set up the same logic with indexes but I think every data should have an id :) Maybe instead of using indexes same logic can be used with song names since they are almost unique. Who knows :)

Pass item data to a react modal

I have a map that render few items and one of its line is below
<a onClick={()=> this.setState({"openDeleteModal":true)}>Delete</a>
Obviously I want to open a modal when user click the delete, but I have to pass a few things like the name of the item, id of the item to perform the deletion. How can I pass says the name to the modal?
I can bind the obj name to a like this
Delete
Am I on the right track?
When working on React applications, try not to think in terms of passing values to other components, but rather updating state that your components are exposed to.
In your example, assuming your modal component is a child of the same component your list of a tags belongs to, you could set the values you are interested in exposing to the modal on the state, as well as updating the property that signals whether the modal is open or not. For example:
class Container extends React.Component {
constructor(props) {
super(props)
this.state = {
openDeleteModal: false,
activeItemName: '', //state property to hold item name
activeItemId: null, //state property to hold item id
}
}
openModalWithItem(item) {
this.setState({
openDeleteModal: true,
activeItemName: item.name,
activeItemId: item.id
})
}
render() {
let buttonList = this.props.item.map( item => {
return (<button onClick={() => this.openModalWithItem(item)}>{item.name}</button>
});
return (
<div>
{/* Example Modal Component */}
<Modal isOpen={this.state.openDeleteModal}
itemId={this.state.activeItemId}
itemName={this.state.activeItemName}/>
{ buttonList }
</div>
)
}
}
Copying over my answer from How to pass props to a modal
Similar scenario
constructor(props) {
super(props)
this.state = {
isModalOpen: false,
modalProduct: undefined,
}
}
//****************************************************************************/
render() {
return (
<h4> Bag </h4>
{this.state.isModalOpen & (
<Modal
modalProduct={this.state.modalProduct}
closeModal={() => this.setState({ isModalOpen: false, modalProduct: undefined})
deleteProduct={ ... }
/>
)
{bag.products.map((product, index) => (
<div key={index}>
<div>{product.name}</div>
<div>£{product.price}</div>
<div>
<span> Quantity:{product.quantity} </span>
<button onClick={() => this.props.incrementQuantity(product, product.quantity += 1)}> + </button>
<button onClick={() => this.decrementQuantity(product)}> - </button> // <----
</div>
</div>
))}
)
}
//****************************************************************************/
decrementQuantity(product) {
if(product.quantity === 1) {
this.setState({ isModalOpen: true, modalProduct: product })
} else {
this.props.decrementQuantity(product)
}
}
Try this: this is the form which has the button, and is a child component of some other component that passes the handleButtonAction method as props, and the button takes the input data and invokes this parent component method
handleSubmit = (e) => {
e.preventDefault();
const data = e.target.elements.option.value.trim();
if (!data) {
this.setState(() => ({ error: 'Please type data' }));
} else {
this.props.handleButtonAction(data, date);
}
}
{this.state.error && <p>{this.state.error}</p>}
<form onSubmit={this.handleSubmit}>
<input type="text" name="option"/>
<div>
<button>Get data</button>
</div>
</form>
The parent component:
handleButtonAction = (data) => {
axios.get(`http://localhost:3000/someGetMethod/${data}`).then(response => {
const resData = response.data;
this.setState({
openModal: true,
status: response.status,
data: resData
});
}).catch((error) => {
if (error.message.toLowerCase() === 'network error') {
this.setStateWithError(-1, {});
}
else { // not found aka 404
this.setStateWithError(error.response.status, '', {currency, date: ddat});
}
});
}

Categories