my modal has a black background when i open it - javascript

I'm doing a table with delete and edit option and when I open a modal my modal has a black background. I found the problem and it is because when I click to open the modal it open multiple times. If I have 10 rows the modal open 10 times.
I'm using React-table-v7 and redux for my modal states.
useTableHook.js
const resourcesColumns = useMemo(() => [
{
Header: 'delete',
accessor: () => 'delete',
disableSortBy: true,
Cell: ({row}) => <div onClick={(event) => event.stopPropagation()}>
<DeleteModal />
</div>
}
], [resourceData]);
useEffect(() => {
hideColumns();
setResourceData(data);
}, [data]);
My tableView.jsx
<tbody {...getTableBodyProps()} >
{loading
? <tr className="text-center">
<td colSpan="5">
<LoadingComponent />
</td>
</tr>
: page.map((row) => {
prepareRow(row);
return (
<tr {...row.getRowProps()} className="tb-row" onClick={() => showSelectedRow(row)} key={row}>
{row.cells.map((cell, index) => <td
{...cell.getCellProps()}
key={index}
className={`${index === 0 ? 'tb-body-txt-left py-2 px-2' : 'tb-body-txt'}`}
>
{cell.render('Cell')}
</td>
)}
</tr>
);
})
}
</tbody>
i'm using a hook for select the state
const modalState = () => {
const ModalState = useSelector((state) => state.modal.isOpen);
return ModalState;
};
my slice for modals
const modalSlice = createSlice({
name: 'modal',
initialState: {isOpen: false},
reducers: {
openModal: (state, action) => {
state.isOpen = true;
},
closeModal: (state, action) => {
state.isOpen = false;
}
}
});
export const {reducer} = modalSlice;
export default modalSlice.actions;
my modal view
<>
<i className="fa fa-trash-can text-primary ZoomIcon" onClick={handlers.opened} />
<Modal show={show} onHide={handlers.closed} className="modalPosition">
<Modal.Header closeButton>
<Modal.Title>Atención !!</Modal.Title>
</Modal.Header>
<Modal.Body>Confirme que desea eliminar el recurso </Modal.Body>
<Modal.Footer className="modalFooter">
<Button variant="light" onClick={handlers.closed} autoFocus={true}>Close</Button>
<Button variant="primary" onClick={handlers.closed} autoFocus={true}>
<i className="fa fa-check" aria-hidden="true"></i>Confirmar</Button>
</Modal.Footer>
</Modal>
</>
here i pass my hook for the show property
const modalState = useDeletehook();
<DeleteModal show={modalState}/>
Do you know how can I fix this problem?

Issue
The main issue is that you've only a single modal.isOpen state and multiple modals.
const modalSlice = createSlice({
name: 'modal',
initialState: { isOpen: true },
reducers: {
openModal: (state, action) => {
state.isOpen = true;
},
closeModal: (state, action) => {
state.isOpen = false;
}
}
});
When state.modal.isOpen is true then all modals are opened.
Solution
You want to set some isOpen state to indicate that a specific modal should be shown. You can do this by using some id value that is related to the row data.
const modalSlice = createSlice({
name: 'modal',
initialState: {
isOpen: null
},
reducers: {
openModal: (state, action) => {
state.isOpen = action.payload;
},
closeModal: (state, action) => {
state.isOpen = null;
}
}
});
To open a specific modal dispatch the openModal action and pass the id of a specific modal you want opened.
dispatch(modalSlice.openModal(rowEl.id));
dispatch(modalSlice.closeModal());
<DeleteModal show={modalState.isOpen === rowEl.id}/>

Related

How to make add to cart page using reactjs and redux

not getting quantity
whenever i push a product inside a cart, the product has been duplicated,
in single product page, if i add a cart then "add to cart" should be change into "go to cart"
this is my cartSlice page.
const CartSlice = createSlice({
name: "cart",
initialState: [],
reducers: {
add(state, action) {
state.push(action.payload);
},
remove(state, action) {
return state.filter((item) => item.id !== action.payload);
},
},
});
this is my singleProduct page
const SingleProduct = () => {
const dispatch = useDispatch();
const { data: products } = useSelector((state) => state.product);
const { productId } = useParams();
const product = products.find((product) => String(product.id) === productId);
const handleAdd = (product) => {
dispatch(add(product));
};
return (
<section className={style.SingleProductSection}>
<div className={style.btns}>
<button
className={style.addToCart}
onClick={() => {
handleAdd(product);
}}
>
<FaCartPlus />
<span>Add to cart</span>
</button>
<Link to="/buyNow">
<button className={style.buyNow}>
<AiFillThunderbolt /> <span>Buy Now</span> {/* buy now page */}
</button>
</Link>
</div>
);
};
here is my cart page
const Cart = () => {
const [total, setTotal] = useState();
const dispatch = useDispatch();
const carts = useSelector((state) => state.cart);
const handleRemove = (productId) => {
return dispatch(remove(productId));
};
useEffect(() => {
setTotal(
carts.reduce(
(acc, curr) => acc + Number(curr.price.toString().split(".")[0]),
0
)
);
}, [carts]);
return (
<>
{carts.map((product) => {
return (
<div className={style.product_cart} quantity={product.quantity}>
<img
src={product.image}
alt="product_image"
className={style.product_image}
/>
<p className={style.product_title}>
{product.title.substring(0, 18)}...
</p>
<p className={style.product_price}>
₹{product.price.toString().split(".")[0]}
</p>
<div className={style.product_quantity}>
<button className="decrement">-</button>
<p>{/* {quantity} */}0</p>
<button className="increment">+</button>
</div>
<button
onClick={() => {
handleRemove(product.id);
}}
>
<AiFillDelete className={style.product_delete_icon} />
</button>
</div>
);
})}
</div>
<div className={style.cartItem_2}>
<p className={style.product_total}>
<span>Total :</span>
<strong>₹{total}</strong>
</p>
<div className={style.cart_buyNow}>
<Link to="/buyNow">
<button className={style.buyNow}>
<AiFillThunderbolt /> <span>Buy Now</span> {/* buy now page */}
</button>
</Link>
</div>
</>
);
};
not getting quantity
whenever i push a product inside a cart, the product has been duplicated,
in single product page, if i add a cart then "add to cart" should be change into "go to cart"
not able to solve this problem,
It's hard to be sure that these are the exact causes but hopefully these points will help a bit.
not getting quantity - try using product.quantity here instead of just quantity
<button className="decrement">-</button>
<p>{/* {quantity} */}0</p> // <-- change here
<button className="increment">+</button>
whenever i push a product inside a cart, the product has been duplicated - you don't want to mutate the store in redux, so try this (remove is fine):
add(state, action) {
return [...state, action.payload];
},
add to cart/go to cart - you could do something like use a piece of state to track if the user has clicked add, if they haven't, do one thing, if they have, do another:
const SingleProduct = () => {
const dispatch = useDispatch();
const { data: products } = useSelector((state) => state.product);
const { productId } = useParams();
const product = products.find((product) => String(product.id) === productId);
// addition
const [hasClickedAdd, setHasClickedAdd] = useState(false)
// addition
const handleAdd = (product) => {
dispatch(add(product));
// addition
setHasClickedAdd(true);
// addition
};
return (
<section className={style.SingleProductSection}>
<div className={style.btns}>
<button
className={style.addToCart}
onClick={() => {
handleAdd(product);
}}
>
<FaCartPlus />
// addition
<span>{hasClickedAdd ? 'Buy now' : 'Add to cart'}</span>
// addition
</button>
<Link to="/buyNow">
<button className={style.buyNow}>
<AiFillThunderbolt /> <span>Buy Now</span> {/* buy now page */}
</button>
</Link>
</div>
);
};
nb, if you do this, you're probably also going to want to change the click handler so that clicking on the 'Buy now' text doesn't just add another one to the cart

React is not updating my state using useEffect and setState

I have the following piece of code: The first one is a modal that holds some inputs:
export const MessagesForm = (props) => {
const [filteredMessages, setFilteredMessages] = useState(props.subject.messages);
const [currentPage, setCurrentPage] = useState(1);
const [recordsPerPage] = useState(10);
const [messages, setMessages] = useState(props.subject.messages);
const indexOfLastRecord = currentPage * recordsPerPage;
const indexOfFirstRecord = indexOfLastRecord - recordsPerPage;
const nPages = Math.ceil(messages.length / recordsPerPage)
const { handleSubmit, reset, register, control, getValues } = useForm({
mode: 'onSubmit',
defaultValues: {
author: '',
message: '',
}
});
useEffect(() => {
const newMessages = messages.slice(indexOfFirstRecord, indexOfLastRecord);
setFilteredMessages(newMessages);
}, [currentPage])
const { remove } = useFieldArray({
control,
name: 'messages',
});
const handleDelete = (id) => {
subjectService.deleteMessage(props.subject['_id']['$oid'], id);
let filters = filteredMessages.filter((el) => el.id !== id);
setFilteredMessages(filters);
}
const handleEdit = (index) => {
const updatedMessage = getValues(`messages.${index}.message`);
const updatedAuthor = getValues(`messages.${index}.author`);
const messageId = getValues(`messages.${index}.id`);
const body = {
message: updatedMessage,
author: updatedAuthor,
id: messageId,
};
subjectService.updateMessage(props.subject['_id']['$oid'], body);
}
const onSubmit = async (formData) => {
const body = {
author: formData.author,
message: formData.message,
}
subjectService.createMessage(props.subject['_id']['$oid'], body)
.then(res => {
Swal.fire({
title: 'Success',
text: 'Message updated successfully',
icon: 'success',
confirmButtonText: 'Close',
confirmButtonColor: '#5CBDA5'
}).then(props.onHide)
})
}
return (
<Modal
{...props}
animation
size="lg"
centered
>
<Modal.Header>
<Modal.Title>
Mensajes
</Modal.Title>
</Modal.Header>
<Modal.Body>
<Pagination
nPages={nPages}
currentPage={currentPage}
setCurrentPage={setCurrentPage}
/>
{
filteredMessages.map((message, index) => {
return (
<div
key={index}
className="message-row"
>
<Form.Group
className='row tes'
>
<div className='col'>
<Form.Label>Author</Form.Label>
<Form.Control
name='id'
style={{ display: 'none' }}
{...register(`messages.${index}.id`)}
defaultValue={message.id}
/>
<Form.Control
type="text"
name='author'
{...register(`messages.${index}.author`)}
defaultValue={message.author}
/>
</div>
<div className='col'>
<Form.Label>Message</Form.Label>
<Form.Control
as="textarea"
name='msg'
{...register(`messages.${index}.message`)}
defaultValue={message.message}
/>
</div>
</Form.Group>
<div className='action-buttons'>
<button className='btn btn-info' type="button" onClick={() => {
Swal.fire({
title: '¿Estás seguro?',
text: "No podrás revertir esto",
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: 'Sí, guardarlo!'
}).then((result) => {
if (result.value) {
handleEdit(index);
}
})
}}><BiSave /></button>
<button className='btn btn-danger' type="button" onClick={() => {
Swal.fire({
title: '¿Estás seguro?',
text: "No podrás revertir esto!",
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: 'Sí, borrarlo!'
}).then((result) => {
if (result.value) {
remove(index);
handleDelete(message.id);
}
})
}}><Trash /></button>
</div>
</div>
)
})
}
</Modal.Body>
<Modal.Footer>
<Button
type="submit"
variant="primary"
form="hook-form"
onClick={() => {
if (props.action !== 'NEW') props.onHide();
}}
>OK</Button>
</Modal.Footer>
</Modal>
)
}
And i'm using a functional component "Pagination" to paginate my modal:
import React from 'react'
const Pagination = ({ nPages, currentPage, setCurrentPage }) => {
const pageNumbers = [...Array(nPages + 1).keys()].slice(1)
const nextPage = () => {
if (currentPage !== nPages) setCurrentPage(currentPage + 1)
}
const prevPage = () => {
if (currentPage !== 1) setCurrentPage(currentPage - 1)
}
return (
<nav>
<ul className='pagination justify-content-center'>
<li className="page-item">
<a className="page-link"
onClick={prevPage}
href='#'>
Previous
</a>
</li>
{pageNumbers.map(pgNumber => (
<li key={pgNumber}
className={`page-item ${currentPage == pgNumber ? 'active' : ''} `} >
<a onClick={() => setCurrentPage(pgNumber)}
className='page-link'
href='#'>
{pgNumber}
</a>
</li>
))}
<li className="page-item">
<a className="page-link"
onClick={nextPage}
href='#'>
Next
</a>
</li>
</ul>
</nav>
)
}
export default Pagination
As my probably wrong understanding, by using the "useEffect" hook listening to the currentPage, it should update the state of my filteredMessages array, but it isn't
By using console log of the newMessages i'm actually obtaining the adequate output of data, but I don't quite know why is this happening only on the console...
You are using messages in your useEffect but you haven't included that in the dependency array.
useEffect(() => {
setFilteredMessages( messages.slice(indexOfFirstRecord, indexOfLastRecord));
}, [currentPage, messages]) // if you add it here it works, but read on
The problem with above code is it would now fire state updates anytime messages or currentPage changes. You could add some logic to look at the previous page but that just overcomplicates things.
My recommendation would be to not perform this type of logic in a useEffect. The best way to handle this scenario is to add this logic directly into nextPage and prevPage event handlers:
const updateFilteredMessages = (page) => {
const indexOfLastRecord = page * recordsPerPage;
const indexOfFirstRecord = indexOfLastRecord - recordsPerPage;
setFilteredMessages(messages.slice(indexOfFirstRecord,
indexOfLastRecord))
}
const nextPage = () => {
if (currentPage !== nPages) {
setCurrentPage(currentPage + 1)
updateFilteredMessages(currentPage + 1)
}
}
All of this logic is related to the nextPage user action, so it's also a lot clearer to encapsulate it within a callback versus splitting off message filtering in the useEffect
I would construct the nextPage & prevPage handlers in your MessageForm component and pass them into Pagination (since you need access to the MessageForm local state)

Why local state also changed when redux store updated

Now I'm building an application using react redux store and local store.
I have two components "tweetTable_Comp" and "likeButton_Comp".
The redux store has all the tweets record "tweets" fetched by an API, and tweetTable_Comp has a local state "filteredTweets" so as to add filter function later and show only selected genre tweets.
And every tweet has likingUserIds.
tweetTable_Comp passes likingUserIds as props to likeButton_Comp so that it can add different style depending on if you already liked the tweet or not.
The problem here is that changing the "tweets[indexNum].likingUserIds" in the redux store when user push like button also affects on the local state "filteredTweets[indexNum].likingUserIds".
I was gonna change the redux info and local state info one by one like in deleteTweet function which already works well.
But this is not intentionally working.
Can anyone teach me why this is happening?
here is reducer.js
redux tweets has objects as below
・title(string)
・text(string)
・createdDate(string)
・likingUserIds(array)
・userId(number)
const defaultState = {
user: {
loggedIn: false,
id: 0,
account: ''
},
tweets: []
}
export default function reducer(state = defaultState, action) {
switch (action.type) {
case 'UPDATE_TWEETS':
return {
...state,
tweets: action.tweets
}
default:
return state;
}
}
here is actions.js
export function getTweets(tweets){
return {
type: 'UPDATE_TWEETS',
tweets: tweets
}
}
here is tweetTable_Comp
class TweetTable_Comp extends Component{
constructor(props){
super(props)
const {dispatch} = props;
this.action = bindActionCreators(actions, dispatch);
this.deleteButtonClicked = this.deleteButtonClicked.bind(this)
this.editButtonClicked = this.editButtonClicked.bind(this)
this.handleChanged = this.handleChanged.bind(this)
this.state = {
filteredTweets: [],
searchWord: ""
}
}
handleChanged(e){
this.setState({[e.target.name]: e.target.value})
}
deleteButtonClicked(id, index){
confirm("削除しますか?") &&
this.deleteTweet(id, index)
}
editButtonClicked(id){
this.props.history.push("/edit/" + id)
}
deleteTweet(id, index){
fetch("http://localhost:8080/twitter/deleteTweet/" + id, {
method: "DELETE"
})
.then((response) => {
if(response.status === 200) {
const newList = this.props.tweets.slice()
newList.splice(index, 1)
this.action.getTweets(newList)
this.setState({filteredTweets: newList})
}
})
}
componentDidMount(){
fetch("http://localhost:8080/twitter/sendAllTweets", {
method: "GET"
})
.then((response) => {
response.json()
.then(json => {
this.action.getTweets(json)
this.setState({filteredTweets: json.slice()})
})
})
}
render(){
return(
<>
<h1 className="text-center">tweet一覧</h1>
<SearchBar searchWord={this.state.searchWord} handleChanged={this.handleChanged}/>
<Container>
<Row>
<Col>
<br/>
<br/>
<Table striped bordered hover>
<thead>
<tr className="text-center">
<th>投稿日</th>
<th>投稿者</th>
<th>タイトル</th>
<th>内容</th>
<th>いいね</th>
<th>削除</th>
<th>編集</th>
</tr>
</thead>
<tbody>
{ this.state.filteredTweets.map((tweet, index) => (
<tr key={tweet.id}>
<td className="text-center">{tweet.createdDate}</td>
<td className="text-center">{tweet.user.account}</td>
<td>{tweet.title}</td>
<td>{tweet.text}</td>
<td className="text-center">
<LikeButton likingUserIds={tweet.likingUserIds} index={index} id={tweet.id} />
</td>
<td className="text-center">
<Button variant="outline-secondary" onClick={() => this.deleteButtonClicked(tweet.id, index)}>
<FontAwesomeIcon icon={faTrashAlt} />
</Button>
</td>
<td className="text-center">
<Button variant="outline-secondary" onClick={() => this.editButtonClicked(tweet.id)}>
<FontAwesomeIcon icon={faEdit} />
</Button>
</td>
</tr>
))
}
</tbody>
</Table>
</Col>
</Row>
</Container>
</>
)
}
}
TweetTable_Comp.propTypes = {
dispatch: PropTypes.func,
tweets: PropTypes.array,
history: PropTypes.object,
user:PropTypes.object
}
function mapStateToProps(state){
return state
}
export default withRouter(connect(mapStateToProps)(TweetTable_Comp))
here is likeButton_Comp
class LikeButton_Comp extends Component {
constructor(props){
super(props)
const {dispatch} = props
this.action = bindActionCreators(actions, dispatch)
this.likeButtonClicked = this.likeButtonClicked.bind(this)
}
likeButtonClicked(func, index){
const data = {
userId:this.props.user.id,
tweetId:this.props.id
}
if(func === "unlike"){
fetch("http://localhost:8080/twitter/like", {
method: "DELETE",
body: JSON.stringify(data)
})
.then((response) => {
if(response.status === 200){
let tweets = this.props.tweets.slice()
const orgLikingUsers = this.props.tweets[index].likingUserIds.slice()
const newLikingUsers = orgLikingUsers.filter(item => item !== this.props.user.id)
tweets[index].likingUserIds = newLikingUsers
this.action.getTweets(tweets)
} else {
alert("処理に失敗しました")
}
})
.catch(error => console.error(error))
} else {
fetch("http://localhost:8080/twitter/like", {
method: "POST",
body: JSON.stringify(data)
})
.then((response) => {
if(response.status === 200){
let tweets = this.props.tweets.slice()
let likingUsers = this.props.tweets[index].likingUserIds.slice()
likingUsers.push(this.props.user.id)
tweets[index].likingUserIds = likingUsers
this.action.getTweets(tweets)
} else {
alert("処理に失敗しました")
}
})
.catch(error => console.error(error))
}
}
render(){
return(
<>
<span>{this.props.likingUserIds.length} </span>
{this.props.tweets.length > 0 && this.props.likingUserIds.includes(this.props.user.id) ?
<Button variant="outline-danger">
<FontAwesomeIcon icon={faHeart} onClick={() => this.likeButtonClicked("unlike", this.props.index)}/>
</Button> :
<Button variant="outline-secondary">
<FontAwesomeIcon icon={faHeart} onClick={() => this.likeButtonClicked("like", this.props.index)}/>
</Button>
}
</>
)
}
}
LikeButton_Comp.propTypes = {
dispatch: PropTypes.func,
user: PropTypes.object,
tweets: PropTypes.array,
likingUserIds: PropTypes.array,
index: PropTypes.number,
id: PropTypes.number
}
function mapStateToProps(state){
return state
}
export default withRouter(connect(mapStateToProps)(LikeButton_Comp))

How to modify a specific component of a list of component rendered using map in react?

I have a PostList component with an array of posts objects. I am rendering this list of post using another pure functional component Post using Array.map() method. Post component has another component - LikeButton to like or unlike a post. Now I want to show a spinner during like or unlike on top of that LikeButton component. LikeButton Component looks something like this:
const LikeButton = (props) => {
const likeBtnClasses = [classes.LikeBtn];
const loggedInUserId = useSelector((state) => state.auth.user.id);
const isLoading = useSelector((state) => state.post.loading);
const isPostLiked = props.post.likes.find(
(like) => like.user === loggedInUserId
);
const [isLiked, setLike] = useState(isPostLiked ? true : false);
const token = useSelector((state) => state.auth.token);
const dispatch = useDispatch();
if (isLiked) {
likeBtnClasses.push(classes.Highlight);
}
const postLikeHandler = () => {
if (!isLiked) {
setLike(true);
dispatch(actions.likePost(props.post._id, token));
} else {
setLike(false);
dispatch(actions.unlikePost(props.post._id, token));
}
};
return isLoading ? (
<Spinner />
) : (
<button
className={likeBtnClasses.join(" ")}
onClick={() => postLikeHandler()}
>
<i class="far fa-thumbs-up"></i>
<small>{props.post.likes.length}</small>
</button>
);
};
Instead of showing the spinner to that single post, I am seeing it on all the posts.
My Post component looks like this:
const Post = (props) => {
return (
<div className={classes.Post}>
<div className={classes.Author}>
<img src={props.postData.avatar} alt="avatar" />
<div className={classes.AuthorDetails}>
<h3>{props.postData.name}</h3>
</div>
</div>
<div className={classes.PostText}>
<p>{props.postData.text}</p>
</div>
<hr />
<div className={classes.PostTools}>
<LikeButton post={props.postData} />
<div className={classes.PostBtn}>
<i class="far fa-comments"></i>
<small>3</small>
</div>
<div className={classes.PostBtn}>
<i class="fas fa-share"></i>
<small>2</small>
</div>
</div>
</div>
);
};
PostList component:
class PostList extends React.Component {
state = {
posts: [
{
text: "POST1",
user: "XYZ",
name: "XYZ",
id: "post1",
likes: [],
},
{
text: "POST2",
user: "johndoe#test.com",
name: "John Doe",
id: "post2",
likes: [],
},
],
};
componentDidMount() {
if (this.props.token) {
this.props.onFetchPosts(this.props.token);
this.props.onFetchUserAuthData(this.props.token);
}
}
render() {
let posts = null;
if (this.props.posts.length === 0) {
posts = this.state.posts.map((post) => {
return <Post key={post.id} postData={post} />;
});
} else {
posts = this.props.posts.map((post) => {
return <Post key={post._id} postData={post} />;
});
}
return (
<div>
<CreatePost />
{posts}
</div>
);
}
}
const mapStateToProps = (state) => {
return {
token: state.auth.token,
posts: state.post.posts,
loading: state.post.loading,
error: state.post.err,
};
};
const mapDispatchToProps = (dispatch) => {
return {
onFetchPosts: (token) => dispatch(actions.fetchPosts(token)),
onFetchUserAuthData: (token) => dispatch(actions.fetchUser(token)),
};
};
Please do some change in your to checking like/unlike is loading or not for the LikeButton.
const LikeButton = (props) => {
....
const [isButtonLoading, setButtonLoading] = useState(false);
...
return isButtonLoading ? (
<Spinner />
) : (
<button
className={likeBtnClasses.join(" ")}
onClick={() => postLikeHandler();setButtonLoading(true)}
>
<i class="far fa-thumbs-up"></i>
<small>{props.post.likes.length}</small>
</button>
);
};
Then on your dispatch callback need to set the isButtonLoading value to false.
const buttonCallback() {
// here we need to reset our flag
setButtonLoading(false);
}
const postLikeHandler = () => {
if (!isLiked) {
setLike(true);
// for this action you need to create third parameter called as callback so after response our buttonCallback will call
dispatch(actions.likePost(props.post._id, token, buttonCallback));
} else {
setLike(false);
// for this action you need to create third parameter called as callback so after response our buttonCallback will call
dispatch(actions.unlikePost(props.post._id, token, buttonCallback);
}
};
fore more details please check here.
Hope this will help you.

React: How to send data on Popup close?

I have this Popup that I built in my React app. It's, in a sense, another page inside the Popup. In this Popup, there is a list. There are no forms at all in this Popup window. I only have another popup inside this popup with a submission form that adds another item to the list.
Therefore, what I'm attempting to do is to submit the list to the Parent component when I click on the "Close" button to close the Popup window. How do I do that?
Here's my code:
SingleBox.js
import React, { Component } from "react";
import SecurityForm from "../SecurityForm/index";
import PriceForm from "../PriceForm/index";
export default class SingleSecuritybox extends Component {
constructor(props) {
super(props);
this.state = {
showPopup: false,
showPricePopup: false, //don't show popup
pricelist: this.props.price
};
}
/* toggle and close popup edit form window */
togglePopup = () => {
this.setState(prevState => ({
showPopup: !prevState.showPopup
}));
};
togglePricePopup = () => {
this.setState(prevState => ({
showPricePopup: !prevState.showPricePopup
}));
};
/* handles edit current security form submissions */
handleEditSecuritySubmission = editSecurity => {
const { editCurrentSecurity, id } = this.props;
this.togglePopup();
editCurrentSecurity({ ...editSecurity, id });
};
updatePrice = updatePrice => {
const { updatePriceList, id } = this.props;
this.togglePricePopup();
updatePriceList({...updatePrice, id});
console.log("editing price", updatePrice);
};
/* handles delete current security form submissions */
handleDeleteSecurity = () => {
const { deleteSecurity, id } = this.props;
// toggle the pop up (close)
this.togglePopup();
// sends the id back to App's "this.deleteSecurity"
deleteSecurity(id);
};
render() {
return (
<div className="box">
<article className="securitytable">
<div className="title">
<h2>
<strong>{this.props.name}</strong>
</h2>
<hr className="lightgray-line" />
</div>
<table>
<tbody>
<tr>
<td className="isin-width">{this.props.isin}</td>
<td className="country-width">{this.props.country}</td>
<td>
<button type="button" className="price-btn" onClick={this.togglePricePopup}>Prices</button>
{this.state.showPricePopup ? (
<PriceForm
pricelist= {this.props.price}
updatePrice={ this.updatePrice }
addPrice={this.props.addPrice}
closePopup= {this.togglePricePopup}
/>
) : null}
</td>
<td className="editing-btn">
<button
type="button"
className="edit-btn"
onClick={this.togglePopup}
>
Edit
</button>
{this.state.showPopup ? (
<SecurityForm
{...this.props}
handleEditSecuritySubmission={ this.handleEditSecuritySubmission }
handleDeleteSecurity={this.handleDeleteSecurity}
cancelPopup={this.togglePopup}
/>
) : null}
</td>
</tr>
</tbody>
</table>
</article>
</div>
);
}
}
This code in question is this list that'll open in Popup window which is a child componenet:
<button type="button" className="price-btn" onClick={this.togglePricePopup}>Prices</button>
{this.state.showPricePopup ? (
<PriceForm
pricelist= {this.props.price}
updatePrice={ this.updatePrice }
addPrice={this.props.addPrice}
closePopup= {this.togglePricePopup}
/>
) : null}
In this child component, which is Price Popup:
import React, { Component } from "react";
import PriceBox from "../SinglePricebox/index";
import AddPriceForm from "../AddPriceForm/index";
export default class PriceForm extends Component {
constructor(props) {
super(props);
this.state = {
priceArr: this.props.pricelist,
showPricePopup: false,
addPricePopup: false,
isToggleOn: true,
date: props.date || "",
number: props.number || ""
};
}
updateInput = ({ target: { name, value } }) =>
this.setState({ [name]: value });
togglePopup = () => {
this.setState(prevState => ({
showPopup: !prevState.showPopup
}));
};
togglePricePopup = () => {
this.setState(prevState => ({
showPricePopup: !prevState.showPricePopup
}));
};
addPricePopup = () => {
this.setState(prevState => ({
addPricePopup: !prevState.addPricePopup
}));
};
/* adds a new price to the list */
addPrice = newPrice => {
this.setState(prevState => ({
addPricePopup: !prevState.addPricePopup,
// spreads out the previous list and adds the new price with a unique id
priceArr: [...prevState.priceArr, { ...newPrice }]
}));
};
handleListSubmit = () => {
const { priceArr } = this.state;
const { updatePrice } = this.props;
const fields = {priceArr};
this.setState(() => {
// if (addPrice) addPrice(fields);
updatePrice(fields);
});
console.log("submission", fields);
};
render() {
return (
<div className="popup">
<div className="popup-inner">
<div className="price-form">
<h2>Prices</h2>
<div className="scroll-box">
{this.state.priceArr.map((props) => (
<PriceBox
{...props}
key={props.date}
/>
))}
</div>
<div className="buttons-box flex-content-between">
<button
type="button"
onClick={this.addPricePopup}
className="btn add-button">Add +</button>
{this.state.addPricePopup && (
<AddPriceForm
addPrice={this.addPrice}
cancelPopup={this.addPricePopup}
/>
)}
<div className="add-btns">
<button
type="button"
onClick={this.handleListSubmit}
className="btn cancel-button"
>
Close
</button>
</div>
</div>
</div>
</div>
</div>
);
}
}
What I'm attempting to do is to send the data (the list array) back to the Parent component on close, but I notice that I can send the data back but I couldn't close the window...
<button
type="button"
onClick={this.handleListSubmit}
className="btn cancel-button"
>
Close
</button>
How do I do this? I cannot add something like this.props.closePopup(); in the handleListSubmit function because while it can close the window, it prevents the list array from being submitted and passed to the Parent component.
You can use parent callback function to send data from child to parent.
on child
handleListSubmit = () => {
...
this.props.onSummited(data)
}

Categories

Resources