How to create a unique instance of an object - reactjs

I fire an onclick called onEditScheduleClick this sets an object which is all fine, however when I click on an edit button on a different row data is overwritten in the props.setScheduleFieldData.
when I click the editButton the 70 will display too 70 and it will have the same dataabase name as ID 156.
As you can see, when I hit the edit button the data is logged fine. I am thinking I may need to create a unique instance for each row? Could someone help me out please!
Schedules
const schedules = () => {
const autoComplete = useContext(AutoCompleteContext)
const snackbar = useContext(SnackbarContext)
const loading = useContext(LoadingContext)
const user = useContext(UserContext)
const [scheduleData, setScheduleData] = useState([])
const [showScheduleModal, setShowScheduleModal] = useState(false)
const [scheduleFieldData, setScheduleFieldData] = useState({})
const [database, setDatabase] = useState('')
useEffect(() => {
async function onLoadScheduleData(){
loading.setLoading(true)
const [results, projectResults,databaseAutoCompleteResults] = await Promise.all([
get('get_testing_schedules', user.user),
get('get_projects_autocomplete/b', user.user),
get('get_databases_autocomplete/a', user.user)
])
autoComplete.setProjectsAutoComplete(projectResults)
autoComplete.setDatabasesAutoComplete(databaseAutoCompleteResults)
setScheduleData(results.data)
loading.setLoading(false)
}
onLoadScheduleData()
}, [])
const sortedScheduleData = [].concat(scheduleData)
.sort((a, b) => a.id < b.id ? 1 : -1)
const showModal = () => {
setShowScheduleModal(true)
setScheduleFieldData({})
}
const onCloseScheduleModal = () => setShowScheduleModal(false)
const onScheduleFieldUpdate = (e, valueFromAutoComplete, nameFromAutoComplete) => {
const name = nameFromAutoComplete ? nameFromAutoComplete
: e.target.name || e.target.getAttribute('name')
const value = valueFromAutoComplete ? valueFromAutoComplete.map(val => val.value).join(',')
: e.target.innerText ? e.target.innerText
: e.target.value
setScheduleFieldData({...scheduleFieldData, ...{[name]: value}})
console.log(name)
}
const onDatabaseSelect = async (e, value) => {
autoComplete.onDatabaseAutoCompleteFieldUpdate(e)
const name = e.target.getAttribute('name')
console.log(name)
setDatabase(value)
setScheduleFieldData({...scheduleFieldData, ...{[name]: value}})
}
console.log(scheduleFieldData, 'scheduleFieldData');
const onSubmitTestingScheduleClick = async () => {
loading.setLoading(true)
const results = await verifiedPost('post_testing_schedule', scheduleFieldData, user.user)
if (results.status === 0) {
setShowScheduleModal(false)
setScheduleFieldData({})
setScheduleData(results.data)
} else if(results.status >= 20 && results.status <= 30) {
user.setSessionTokenMatches(false)
}
snackbar.statusCheck(results)
loading.setLoading(false)
}
return (!scheduleData ? null :
<PageWrapper title='Schedules'>
<div style={{marginBottom: '20px'}}>
<Button
onClick={showModal}
className='Button Dark Main'
text='CREATE SCHEDULE'
/>
</div>
<div className='Card'>
<div className='TableTopbar ScheduleGrid'>
<div>ID</div>
<div>Interval</div>
<div>Project ID</div>
<div>Database</div>
<div>Create Timestamp</div>
<div>Create User Id</div>
<div>Edit</div>
</div>
{sortedScheduleData.map(schedule =>
<ScheduleDataRow
key={schedule.id}
schedule={schedule}
setScheduleData={setScheduleData}
onScheduleFieldUpdate={onScheduleFieldUpdate}
setScheduleFieldData={setScheduleFieldData}
scheduleFieldData={scheduleFieldData}
onDatabaseSelect={onDatabaseSelect}
/>
)}
</div>
<ScheduleModal
showScheduleModal={showScheduleModal}
onCloseScheduleModal={onCloseScheduleModal}
onScheduleFieldUpdate={onScheduleFieldUpdate}
scheduleFieldData={scheduleFieldData}
onSubmitTestingScheduleClick={onSubmitTestingScheduleClick}
scheduleFieldData={scheduleFieldData}
// onFieldUpdate={onFieldUpdate}
onDatabaseSelect={onDatabaseSelect}
database={database}
/>
</PageWrapper>
)
}
export default schedules
ScheduleDataRow:
const scheduleDataRow = props => {
const testing = useContext(TestingFrameworkContext)
const autoComplete = useContext(AutoCompleteContext)
const snackbar = useContext(SnackbarContext)
const loading = useContext(LoadingContext)
const user = useContext(UserContext)
const [usageMode, setUsageMode] = useState('Read')
const onEditScheduleClick = () => {
props.setScheduleFieldData({
'id': props.schedule.id,
'interval': props.schedule.interval,
'project_id': props.schedule.project_id,
'database': props.schedule.database.toString(),
})
setUsageMode('Edit')
}
const onSubmitTestingScheduleClick = async () => {
loading.setLoading(true)
const results = await verifiedPost('post_testing_schedule', props.scheduleFieldData, user.user)
if (results.status === 0) {
props.setScheduleData(results.data)
setUsageMode('Read')
} else if(results.status >= 20 && results.status <= 30) {
user.setSessionTokenMatches(false)
}
snackbar.statusCheck(results)
loading.setLoading(false)
}
const onCancelEditScheduleClick = () => setUsageMode('Read')
const getPartialDatabase = (database) => {
const split = database.split('::')
return split[split.length - 1]
}
return (
<div className='Table ScheduleGrid'>
{usageMode === 'Read' ?
<React.Fragment>
<div>{props.schedule.id}</div>
<div>{props.schedule.interval}</div>
<div>{props.schedule.project_id}</div>
<div>{getPartialDatabase(props.schedule.database)}</div>
<div>{props.schedule.create_timestamp}</div>
<div>{props.schedule.create_user_id}</div>
<div>
<EditIcon
style={{padding: '2px', width: '0.8em', height: '0.8em', marginRight: '5px'}}
className='CircleButton'
onClick={onEditScheduleClick}
/>
</div>
</React.Fragment>
:
<React.Fragment>
<div>{props.schedule.id}</div>
{configs.map((config, k) => {
const Field = config.field
return (
<div key={config.name} className='Main'>
<Field
uniqueIdentifier={config.name}
placeholder={config.label}
name={config.name}
uniqueIdentifier={k}
onChange={props.onScheduleFieldUpdate}
onSelect={props.onScheduleFieldUpdate}
onAutoCompleteOnChange={autoComplete.onProjectAutoCompleteFieldUpdate}
options={config.name === 'project_id' ? autoComplete.projectsAutoComplete : config.name === 'database' ? autoComplete.databasesAutoComplete : config.options}
value={props.scheduleFieldData[config.name]}
initialValues={
props.scheduleFieldData[config.name].split(',').filter(x => x).map(x => ({label: x, value: x}))
}
/>
</div>
)
})}
<div><AutoCompleteSingle
name='database'
label='Database'
options={autoComplete.databasesAutoComplete}
onChange={autoComplete.onDatabaseAutoCompleteFieldUpdate}
onSelect={props.onDatabaseSelect}
initialValue={props.scheduleFieldData['database']}
uniqueIdentifier={1}
triggerOnSelectOnBackspace
/></div>
<div>{props.schedule.create_timestamp}</div>
<div>{props.schedule.create_user_id}</div>
<div style={{display: 'flex', alignItems: 'flex-start'}}>
<div>
<Button
style={{marginRight: '10px'}}
text='Submit'
disabled={false}
className='Button Dark Main'
onClick={onSubmitTestingScheduleClick}
/>
</div>
<div>
<Button
text='Cancel'
className='Button Dark Main'
onClick={onCancelEditScheduleClick}
/>
</div>
</div>
</React.Fragment>
}
</div>
)
}
scheduleDataRow.propTypes = {
schedule: PropTypes.object,
setScheduleData: PropTypes.func
}
export default scheduleDataRow

Related

How to remove item in array have 2 id, id product and id product detail

enter image description here
enter image description here
enter image description here
API data
How to remove item in array have 2 id, id product and id product detail. When I click on the product to delete, redux selects the id in array 1 to delete and delete all product details. I want to remove product detail instead of product in array 1
This is my cartSlice.js in redux
import { createSlice } from '#reduxjs/toolkit';
const cartSlice = createSlice({
name: 'cart',
initialState: {
cart: [],
},
reducers: {
addToCart: (state, action) => {
const itemInCart = state.cart.find((item) => item.id === action.payload.id && item.idProduct === action.payload.idProduct);
if (itemInCart) {
itemInCart.quantity++;
} else {
state.cart.push({ ...action.payload, quantity: 1 });
}
},
incrementQuantity: (state, action) => {
const item = state.cart.find((item) => item.id === action.payload);
item.quantity++;
},
decrementQuantity: (state, action) => {
const item = state.cart.find((item) => item.id === action.payload);
if (item.quantity === 1) {
item.quantity = 1
} else {
item.quantity--;
}
},
removeItem: (state, action) => {
const removeItem = state.cart.filter((item) =>
item.id !== action.payload
);
state.cart = removeItem;
},
},
});
export const cartReducer = cartSlice.reducer;
export const {
addToCart,
incrementQuantity,
decrementQuantity,
removeItem,
} = cartSlice.actions;
SingleProduct.js
import { useDispatch } from 'react-redux';
import { useSelector } from 'react-redux';
import { addToCart } from './redux/cartSlice';
function SingleProduct() {
const [index, setIndex] = useState(1);
const [product, setProduct] = useState([]);
const [productDetail, setProductDetail] = useState()
// Set option product
const [id, setId] = useState(1)
const [idProduct, setIdProduct] = useState(1)
const [name, setName] = useState('')
const [price, setPrice] = useState(0);
const [image, setImage] = useState(undefined)
const [colorValue, setColorValue] = useState(undefined)
const [ramValue, setRamValue] = useState(undefined)
const [storageValue, setStorageValue] = useState(undefined)
const cart = useSelector((state) => state.cart)
console.log(cart)
const dispatch = useDispatch()
// const getTotalQuantity = () => {
// let total = 0
// cart.forEach(item => {
// total += item.quantity
// })
// return total
// }
// get path
const location = useLocation()
const productId = location.pathname.split('/')[2]
const path = location.pathname
console.log(productId, path)
//fetch api product
useEffect(() => {
fetch(`https://api-uit.herokuapp.com/api/iphone/${productId}`)
.then(res => res.json())
.then(data => {
setProduct(data);
setName(data.name)
setId(data.id)
setProductDetail(data.product_details)
var dataFirst = data.product_details[0]
var checkRam = dataFirst.ram !== undefined ? `${dataFirst.ram}` : undefined
setRamValue(checkRam)
setPrice(dataFirst.price)
setStorageValue(dataFirst.storage)
setImage(dataFirst.image)
setColorValue(dataFirst.color)
})
}, [productId])
const product_current = {
id: id,
idProduct: idProduct,
name: name,
image: image,
price: price,
color: colorValue,
ram: ramValue,
storage: storageValue
};
console.log(product_current)
//handle option
const findColor = (color) => {
setColorValue(color);
}
const findRam = (ram) => {
setRamValue(ram);
}
const findStorage = (storage) => {
setStorageValue(storage);
}
// console.log(productDetail)
// handle render when choose option
useLayoutEffect(() => {
var target = productDetail && productDetail.find(item => {
var ram = ramValue === undefined ? ramValue : `${ramValue}`;
return (
item.ram === ram &&
item.color === `${colorValue}` &&
item.storage === `${storageValue}`
)
})
// console.log(target)
var changeName = target !== undefined ? (target.name) : name;
var changeId = target !== undefined ? (target.id) : idProduct;
var changeImage = target !== undefined ? (target.image) : image;
var changePrice = target !== undefined ? (target.price) : price;
setIdProduct(changeId)
setName(changeName)
setImage(changeImage)
setPrice(changePrice)
// console.log(result);
}, [productDetail, ramValue, colorValue, storageValue, price, image, name, idProduct])
const getText = (html) => {
const doc = new DOMParser().parseFromString(html, "text/html")
return doc.body.textContent
}
const priceString = getText(price.toLocaleString().concat('đ'))
return (
<div className={cx('wrap')}>
<div className="grid wide">
<div className="wide row">
<div className="wide l-6 m-6 c-12" >
<img className={cx('img-product')} src={(image)} alt={product.name} />
</div>
<div className="wide l-6 m-6 c-12">
<div className={cx("wrap-heading")}>
<div className={cx('heading')}>{name}</div>
<div className={cx('separate')}></div>
<div className={cx('price')}>{priceString}</div>
{/* Check product have storage */}
{product.option && product.option.map((option, idx) =>
<div key={idx}>
{(option.key === "storage" ?
<div className={cx('wrap-storage')}>
<div className={cx('storage-heading')}>Chọn {option.key}</div>
<div className={cx('wrap-option')}>
{option.value.map((value, idx) =>
<div className={cx('option', `${value === storageValue ? "active" : ""}`)}
key={idx}
value={value}
onClick={() => findStorage(value)}
>
{value}
</div>
)}
</div>
</div> : <></>)}
</div>
)}
{/* Check product have ram */}
{product.option && product.option.map((option, idx) =>
<div key={idx}>
{(option.key === "ram" ?
<div className={cx('wrap-storage')}>
<div className={cx('storage-heading')}>Chọn {option.key}</div>
<div className={cx('wrap-option')}>
{option.value.map((value, idx) =>
<div className={cx('option', `${value === ramValue ? "active" : ""}`)}
key={idx}
value={value}
onClick={() => findRam(value)}
>
{value}
</div>
)}
</div>
</div> : <></>)}
</div>
)}
{/* Check product have color */}
{product.option && product.option.map((option, idx) =>
<div key={idx}>
{(option.key === "color" ?
<div className={cx('wrap-color')}>
<div className={cx('heading-color')}>Chọn {option.key}</div>
<div className={cx('option-color')}>
{option.value.map((value, idx) =>
<div className={cx('space', `${value === colorValue ? "active" : ""}`)}
key={idx}
value={value}
onClick={() => findColor(value)}
>
<div className={cx('radio-color')} style={{ backgroundColor: value }}></div>
</div>
)}
</div>
</div> : <></>)}
</div>
)}
<div className={cx('wrap-buy')}>
<div className={cx('box-buy')}>
<div className={cx('buy-cash')}>MUA NGAY</div>
</div>
<div className={cx('box-buy')}>
<div className={cx('add-to-list')}
onClick={() => dispatch(addToCart(product_current))}
>THÊM VÀO GIỎ HÀNG</div>
</div>
</div>
</div>
</div>
)
}
export default SingleProduct
This is my Cart.js
import React, { useState } from 'react'
import classNames from "classnames/bind";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { useSelector } from 'react-redux';
import { useDispatch } from 'react-redux'
import { faXmark, faBagShopping } from "#fortawesome/free-solid-svg-icons";
import styles from './Cart.module.scss'
import { removeItem } from 'src/pages/SingleProduct/redux/cartSlice'
const cx = classNames.bind(styles)
function Cart() {
const dispatch = useDispatch()
const [showCart, setShowCart] = useState(false)
// toggle cart
const ToggleCart = () => {
setShowCart(!showCart)
}
const show = showCart ? 'toggle-cart' : ''
const cart = useSelector((state) => state.cart)
// console.log(cart)
// Tính tổng
const getTotal = () => {
let totalQuantity = 0
let totalPrice = 0
cart.forEach(item => {
totalQuantity += item.quantity
totalPrice += item.price * item.quantity
})
return { totalPrice, totalQuantity }
}
const getText = (html) => {
const doc = new DOMParser().parseFromString(html, "text/html")
return doc.body.textContent
}
const totalPrice = getTotal().totalPrice
const priceTotalString = getText(totalPrice.toLocaleString().concat('đ'))
return (
<>
<div className={cx('wrap-cart')}>
<FontAwesomeIcon className={cx('icon')} icon={faBagShopping} onClick={ToggleCart} />
<p>{getTotal().totalQuantity || 0}</p>
</div>
{show && <div className={cx('wrap-modal')} onClick={ToggleCart}>
</div>}
<div className={cx('cart', show)}>
<FontAwesomeIcon icon={faXmark} className={cx('icon-mark-cart')} onClick={ToggleCart} />
<div className={cx('height-cart')}>
{cart.map((product, idx) =>
<div className={cx('product-cart')} key={idx}>
<img className={cx('cart-img')} src={product.image} alt="" />
<div className={cx('wrapper-cart')}>
<div className={cx('name-cart')}>{product.name}</div>
<p className={cx('price-cart')}>{product.quantity} x {getText(product.price.toLocaleString().concat('đ'))}</p>
</div>
<div
className={cx('cartItem__removeButton')}
onClick={() => dispatch(removeItem(product.id && product.idProduc))}>
x
</div>
</div>
)}
</div>
<div className={cx('footer-cart')}>
<div className={cx('wrap-total')}>
<p>Tạm tính: </p>
<p>{priceTotalString}</p>
</div>
<div className={cx('wrap-pay')}>
<div className={cx('cart-buy')}>MUA NGAY</div>
</div>
</div>
</div>
</>
)
}
export default Cart
you should pass both the id (productId & productDetailId) to removeItem reducer function.
removeItem: (state, action) => {
const {productId, productDetailId} = action.payload;
const selectedProduct = state.cart.find((item) => item.id === productId);
if(Array.isArray(selectedProduct?.product_details)) {
selectedProduct.product_details = selectedProduct.product_details.filter(item => item.id !== productDetailId);
}
},
Basically, what we are doing is:
find product with the given product id
In the selected(found) product, remove the product detail id.
To call call this:
dispatch(removeItem({
productId: "yyy",
productDetailId: "zzz"
}))
Hope, that'll solve the issue.
Thanks

Warning: Failed prop type: The prop `onSubmit` is marked as required in `ProjectWizardForm`, but its value is `undefined`

This error is shown in the browser console when Dialog (modal) is open:
Warning: Failed prop type: The prop onSubmit is marked as required
in ProjectWizardForm, but its value is undefined.
Code:
const UstHomeToolbar = ({ destroyProjectWizardForm, handleOpenAddPattern }) => {
const [open, setOpen] = useState(false);
const [openUploadForm, setOpenUploadForm] = useState(false);
const [projectWizardModalTitle, setProjectWizardModalTitle] = useState("");
const classes = useStyles();
const [selectedFile, setSelectedFile] = useState();
const [isFilePicked, setIsFilePicked] = useState(false);
const [statusText, setStatusText] = useState({
type: null,
msg: null,
});
const [userData, setUserData] = useState({});
useEffect(() => {
const userDataForPermissions = localStorage.getItem(LOGGED_IN_USER);
setUserData(JSON.parse(userDataForPermissions));
}, []);
const handleProjectDialogClickOpen = () => {
setOpen(true);
};
const handleProjectDialogClose = async () => {
setOpen(false);
};
const changeUploadHandler = (event) => {
setSelectedFile(event.target.files[0]);
setIsFilePicked(true);
};
const clearUploadHandler = (event) => {
setSelectedFile(null);
setIsFilePicked(false);
};
const handleUploadSubmission = () => {
const formData = new FormData();
formData.append("excel", selectedFile);
axios
.post("/project/excel", formData, {
headers: { "Content-Type": "multipart/form-data" },
})
.then((res) => {
clearUploadHandler();
setStatusText({
type: "success",
msg: "Excel file is uploaded successfully and taking you to the design....",
});
const design = res.data;
window.location.href = `/project/${design.project_id}/design/${design.id}`;
})
.catch((err) => {
console.error("Error:", err?.response?.data?.message);
clearUploadHandler();
setStatusText({
type: "error",
msg: err?.response?.data?.message,
});
});
};
const handleProjectDialogExited = () => {
destroyProjectWizardForm();
};
const handleProjectWizardModalTitle = (newModalTitle) =>
setProjectWizardModalTitle(newModalTitle);
return (
<Toolbar className={classes.toolbarHome}>
<Grid container justify="space-between">
<Grid item>
<Box className={classes.projectTitleStyle}>Projects</Box>
<Box className={classes.projectTitleDetailsStyle}>
List of ongoing and finished projects
</Box>
</Grid>
<Grid item>
<div className={classes.addProjectButtonStyle}>
<IxButton
onClick={() => setOpenUploadForm(true)}
handleClose={() => setOpenUploadForm(false)}
customClassName={TOOLBAR_BUTTON}
text="UPLOAD EXCEL"
inline={true}
disabled={!getPermission(userData, PROJECT).can_edit}
/>
<IxButton
onClick={handleProjectDialogClickOpen}
customClassName={TOOLBAR_BUTTON}
text="ADD PROJECT"
inline={true}
disabled={!getPermission(userData, PROJECT).can_edit}
/>
</div>
</Grid>
</Grid>
<IxDialog
handleClose={() => setOpenUploadForm(false)}
open={openUploadForm}
modalTitle={"Upload Design Excel"}
>
<div style={{ minHeight: "50px" }}>
Select an excel (*.xls) file:
<input
type="file"
onChange={changeUploadHandler}
accept=".xls"
style={{ marginLeft: "10px" }}
/>
</div>
<div style={{ marginBottom: "5px" }}>
{statusText.type && (
<Alert className={classes.statusText} severity={statusText.type}>
{statusText.msg}{" "}
</Alert>
)}
</div>
<div>
<Button
variant="contained"
component="label"
onClick={handleUploadSubmission}
disabled={!isFilePicked}
>
Upload File
</Button>
</div>
</IxDialog>
<IxDialog
handleClose={handleProjectDialogClose}
onExited={handleProjectDialogExited}
open={open}
modalTitle={projectWizardModalTitle}
>
<ProjectWizardForm
handleOpenAddPattern={handleOpenAddPattern}
handleClose={handleProjectDialogClose}
handleProjectWizardModalTitle={handleProjectWizardModalTitle}
/>
</IxDialog>
</Toolbar>
);
};
export default UstHomeToolbar;
ProjectWizardForm component:
const ProjectWizardForm = props => {
const [page, setPage] = useState(1);
const [touchedOnLoad, setTouchedOnLoad] = useState(false);
const history = useHistory();
const {
handleClose,
dispatch,
handleOpenAddPattern,
handleProjectWizardModalTitle
} = props;
useEffect(() => {
switch (page) {
case 1:
handleProjectWizardModalTitle('Add project');
break;
case 2:
handleProjectWizardModalTitle('Add target specs');
break;
case 3:
handleProjectWizardModalTitle('Add version details');
break;
default:
return;
}
}, [page, handleProjectWizardModalTitle]);
const saveData = values => {
const { submitButtonType } = values;
var errors = ProjectValidate(values);
if (Object.entries(errors).length > 0) {
if (errors?.number || errors?.drawn_date) {
setTouchedOnLoad(true);
setPage(1);
return;
} else if (errors.mandrel_id) {
setTouchedOnLoad(true);
setPage(3);
return;
}
}
const openAddPatternForm = newProject => {
const openAddPatternForm = true;
handleOpenAddPattern(openAddPatternForm, newProject);
};
dispatch(
addDesign(
formatProjectWizardSubmittedValue(values),
(err, newProject) => {
if (!err) {
dispatch(fetchProjects());
submitButtonType === ADD_PATTERN_NEXT_SUBMIT_BUTTON_TYPE
? openAddPatternForm(newProject)
: history.push(
`/project/${newProject.project_id}/details/${newProject.id}`
);
} else {
dispatch(setError(getFormatError(err)));
}
}
)
);
handleClose();
};
return (
<div>
{page === 1 && (
<UstProjectDetailForm
getDialogActionButtons={getAddProjectDetailsDialogActionButtons}
{...props}
touchedOnLoad={touchedOnLoad}
onSubmit={saveData}
nextPage={() => setPage(page + 1)}
/>
)}
{page === 2 && (
<UstProjectTargetSpecForm
{...props}
getDialogActionButtons={getAddTargetSpecDialogActionButtons}
onSubmit={saveData}
previousPage={() => setPage(page - 1)}
nextPage={() => setPage(page + 1)}
/>
)}
{page === 3 && (
<UstDesignDetailForm
{...props}
getDialogActionButtons={getAddDesignDetailsDialogActionButtons}
touchedOnLoad={touchedOnLoad}
onSubmit={saveData}
previousPage={() => setPage(page - 1)}
nextPage={() => setPage(page + 1)}
/>
)}
</div>
);
};
ProjectWizardForm.propTypes = {
onSubmit: PropTypes.func.isRequired
};
How can I fix this issue?

change menu in nextJS / reactJS

here I have a case where when I click a link "create custom url" it will change to "cancel" and "save".
for example like this :
When I click "create custom url"
Turned into like this
Want it like that.
However, when I try to make it this is the result
Cancel and save together, cancel and save should have its own function.
This is my code:
const ShareKey = ({ surveyId, shareKey = "", setShareKey, refProps }) => {
const [isLoading, setIsLoading] = useState(false);
const [customInput, setCustomInput] = useState(process.env.NEXT_PUBLIC_SHARE_URL + "/");
const [active, setActive] = useState(false);
const [show, setShow] = useState(false);
const getLink = () => {
setShareKey("");
setIsLoading(true);
automaticApproval({ surveyId })
.then((resolve) => {
console.log(resolve);
setShareKey(resolve.key);
})
.catch((reject) => {
console.log(reject);
toast.error(reject);
})
.finally(() => {
setIsLoading(false);
});
};
return (
<section>
<button onClick={getLink} disabled={isLoading} className={styles.link_button}>
{`GENERATE ${shareKey && "NEW "}LINK`}
</button>
{shareKey && (
<div className={styles.link_container}>
<label>Share link</label>
<div className={styles.link}>
<input
value={active ? customInput : process.env.NEXT_PUBLIC_SHARE_URL + "/" + shareKey}
onChange={(e) => {
setCustomInput(e.target.value);
}}
disabled={!active}
ref={refProps}
/>
{!show &&
<button
onClick={() => {
if (typeof navigator !== "undefined") {
navigator.clipboard.writeText(refProps.current.value);
toast.info("Copied to clipboard");
}
}}
>
copy
</button>
}
</div>
<p className={styles.custom} onClick={() => {setActive(!active); setShow(!show);}}>
{active ? "Cancel " + " Save" : "Create Custom URL"}
</p>
</div>
)}
</section>
);
};
Is there something wrong in my code? Please help. Thank you
This is how you gonna do it.
Also I changed your <p> to <div>. You can't nest anything inside of it.
You can change the container <div> to React Fragment if you don't want have some additional div.
You also have to fix the style.
const ShareKey = ({ surveyId, shareKey = "", setShareKey, refProps }) => {
const [isLoading, setIsLoading] = useState(false);
const [customInput, setCustomInput] = useState(
process.env.NEXT_PUBLIC_SHARE_URL + "/"
);
const [active, setActive] = useState(false);
const [show, setShow] = useState(false);
const getLink = () => {
setShareKey("");
setIsLoading(true);
automaticApproval({ surveyId })
.then((resolve) => {
console.log(resolve);
setShareKey(resolve.key);
})
.catch((reject) => {
console.log(reject);
toast.error(reject);
})
.finally(() => {
setIsLoading(false);
});
};
const toggleFunction = () => {
setActive(!active);
setShow(!show);
};
return (
<section>
<button
onClick={getLink}
disabled={isLoading}
className={styles.link_button}
>
{`GENERATE ${shareKey && "NEW "}LINK`}
</button>
{shareKey && (
<div className={styles.link_container}>
<label>Share link</label>
<div className={styles.link}>
<input
value={
active
? customInput
: process.env.NEXT_PUBLIC_SHARE_URL + "/" + shareKey
}
onChange={(e) => {
setCustomInput(e.target.value);
}}
disabled={!active}
ref={refProps}
/>
{!show && (
<button
onClick={() => {
if (typeof navigator !== "undefined") {
navigator.clipboard.writeText(refProps.current.value);
toast.info("Copied to clipboard");
}
}}
>
copy
</button>
)}
</div>
{active ? (
<div>
<button onClick={toggleFunction}>Cancel</button>
<button onClick={yourSaveFunction}>Save</button>
</div>
) : (
<div className={styles.custom} onClick={toggleFunction}>
Create Custom URL
</div>
)}
</div>
)}
</section>
);
};

I need to click twice before data returns in console properly

In this react component, I want to render the data into my console when the 'TO FROM SELECTED UNITS' button is clicked by the user, but the button has to be pressed twice for the data to show in the console.
Please ignore:
It looks like your post is mostly code; please add some more details.
It looks like your post is mostly code; please add some more details.
It looks like your post is mostly code; please add some more details.
It looks like your post is mostly code; please add some more details.
Here is my code:
const unitTests = props => {
const autoComplete = useContext(AutoCompleteContext)
const loading = useContext(LoadingContext)
const snackbar = useContext(SnackbarContext)
const user = useContext(UserContext)
const query = new URLSearchParams(props.location.search)
const targetObjectFromURL = query.get('object')
const cookies = new Cookies()
const [unitTestsData, setUnitTestsData] = useState(null)
const [database, setDatabase] = useState('')
const [targetObject, setTargetObject] = useState(null)
const [unitTestsMode, setUnitTestsMode] = useState('Object')
const [AddRemoveDatabaseChanges, setAddRemoveDatabaseChanges] = useState(null)
const [AddRemoveDatabaseMode, setAddRemoveDatabaseMode] = useState('ADD')
console.log(unitTestsData);
useEffect(() => {
async function onLoadUnitTests() {
loading.setLoading(true)
const [objectsAutoCompleteResults, databaseAutoCompleteResults] = await Promise.all([
get('get_objects_autocomplete/all/all/b', user.user),
get('get_databases_autocomplete/a', user.user)
])
autoComplete.setObjectsAutoComplete(objectsAutoCompleteResults)
autoComplete.setDatabasesAutoComplete(databaseAutoCompleteResults)
if(targetObjectFromURL) setTargetObject(targetObjectFromURL)
else if(cookies.get('table')) setTargetObject(cookies.get('table'))
loading.setLoading(false)
}
onLoadUnitTests()
}, [])
useEffect(() => {
async function getUnitTests() {
loading.setLoading(true)
const optionalParams = unitTestsMode === 'Object'
? `?type=object&target=${targetObject}`
: `?type=database&target=${database}`
const url = `get_unit_tests${optionalParams}`
const results = await verifiedGet(url, user.user)
if(results.status === 0) {
setUnitTestsData(results.data)
} else if(results.status >= 20 && results.status <= 30) {
snackbar.statusCheck(results)
user.setSessionTokenMatches(false)
} else snackbar.statusCheck(results)
loading.setLoading(false)
}
if(targetObject || database) getUnitTests()
}, [targetObject,database])
const onObjectSelect = (e, value) => {
props.history.push(`/unittests?object=${value}`)
setTargetObject(value)
cookies.set('table', value, { path: '/'})
}
const onNewTestCaseClick = () => {
props.history.push(`/createtest?object=${targetObject}`)
}
const onDatabaseSelect = (e, value) => setDatabase(value)
const onChangeUnitTestsMode = (e) => setUnitTestsMode(e.target.getAttribute('value'))
const onChangeAddRemoveMode = (e) => setAddRemoveDatabaseMode(e.target.getAttribute('value'))
console.log(AddRemoveDatabaseMode);
const addtoproduction = () => {
let databaseChanges = unitTestsData.map(tests => {
return {
"unit_test_id": tests.unit_test_template_id,
"databases": tests.databases
}
})
setAddRemoveDatabaseChanges(databaseChanges)
if(AddRemoveDatabaseChanges != null && AddRemoveDatabaseMode === 'ADD'){
console.log('added data', AddRemoveDatabaseChanges);
} else if (AddRemoveDatabaseChanges != null && AddRemoveDatabaseMode === 'REMOVE') {
console.log('removed data', AddRemoveDatabaseChanges )
}
}
return (
<PageWrapper title='View Unit Tests'>
<div className='Main UnitTestsAutoCompleteWrapper' style={{display: 'flex', justifyContent: 'center', alignItems: 'center'}}>
<div style={{width: '30%', marginRight: '10px'}}>
<DropdownSingle
options={['Object', 'Database']}
value='Object'
name='Search unit tests'
onChange={onChangeUnitTestsMode}
/>
</div>
{unitTestsMode === 'Object' ?
<div>
<AutoCompleteSingle
name='Table'
label='Table'
options={autoComplete.objectsAutoComplete}
onChange={autoComplete.onObjectAutoCompleteFieldUpdate}
onSelect={onObjectSelect}
uniqueIdentifier={0}
initialValue={targetObject}
/>
</div> :
<div style={{display: 'flex', justifyContent: 'space-between' }}>
<div style={{width: '100%'}}>
<DropdownSingle
options={['ADD', 'REMOVE']}
value='ADD'
name='Search unit tests'
onChange={onChangeAddRemoveMode}
/>
</div>
<AutoCompleteSingle
name='Database'
label='Database'
options={autoComplete.databasesAutoComplete}
onChange={autoComplete.onDatabaseAutoCompleteFieldUpdate}
onSelect={onDatabaseSelect}
uniqueIdentifier={1}
style={{width: '1000%'}}
/>
<div>
<Button
text='TO/FROM SELECTED UNIT TESTS'
onClick={addtoproduction}
/>
</div>
</div>
}
</div>
{!unitTestsData ? null :
!unitTestsData.length ?
<div>
This table has no associated unit tests
<div style={{marginTop: '20px'}}>
<Button
text='New Test Case'
icon={<AddIcon className='ButtonIcon' />}
onClick={onNewTestCaseClick}
className='Button Dark Main'
style={{width: '200px'}}
/>
</div>
</div>
:
unitTestsMode === 'Object' ?
<UnitTestsObjectView unitTestsData={unitTestsData}/>
: <UnitTestsDatabaseView unitTestsData={unitTestsData} />
}
{!targetObject ? null :
<div style={{position: 'fixed', bottom: '80px', right: '40px'}}>
<Button
text='New Test Case'
icon={<AddIcon className='ButtonIcon' />}
onClick={onNewTestCaseClick}
className='Button Dark Main'
/>
</div>
}
</PageWrapper>
)
}
export default withRouter(unitTests)

How to pass usestate data to function

I am trying to pass my unitTestData into the addtoproduction function however the console log is returning with null and I am not to sure why. The addtoproduction function is highlighted to asteriks
const unitTests = props => {
const autoComplete = useContext(AutoCompleteContext)
const loading = useContext(LoadingContext)
const snackbar = useContext(SnackbarContext)
const user = useContext(UserContext)
const query = new URLSearchParams(props.location.search)
const targetObjectFromURL = query.get('object')
const cookies = new Cookies()
const [unitTestsData, setUnitTestsData] = useState(null)
const [database, setDatabase] = useState('')
const [targetObject, setTargetObject] = useState(null)
const [unitTestsMode, setUnitTestsMode] = useState('Object')
useEffect(() => {
async function onLoadUnitTests() {
loading.setLoading(true)
const [objectsAutoCompleteResults, databaseAutoCompleteResults] = await Promise.all([
get('get_objects_autocomplete/all/all/b', user.user),
get('get_databases_autocomplete/a', user.user)
])
autoComplete.setObjectsAutoComplete(objectsAutoCompleteResults)
autoComplete.setDatabasesAutoComplete(databaseAutoCompleteResults)
if(targetObjectFromURL) setTargetObject(targetObjectFromURL)
else if(cookies.get('table')) setTargetObject(cookies.get('table'))
loading.setLoading(false)
}
onLoadUnitTests()
}, [])
useEffect(() => {
async function getUnitTests() {
loading.setLoading(true)
const optionalParams = unitTestsMode === 'Object'
? `?type=object&target=${targetObject}`
: `?type=database&target=${database}`
const url = `get_unit_tests${optionalParams}`
const results = await verifiedGet(url, user.user)
if(results.status === 0) {
setUnitTestsData(results.data)
} else if(results.status >= 20 && results.status <= 30) {
snackbar.statusCheck(results)
user.setSessionTokenMatches(false)
} else snackbar.statusCheck(results)
loading.setLoading(false)
}
if(targetObject || database) getUnitTests()
}, [targetObject,database])
const onObjectSelect = (e, value) => {
props.history.push(`/unittests?object=${value}`)
setTargetObject(value)
cookies.set('table', value, { path: '/'})
}
const onNewTestCaseClick = () => {
props.history.push(`/createtest?object=${targetObject}`)
}
const onDatabaseSelect = (e, value) => setDatabase(value)
const onChangeUnitTestsMode = (e) => setUnitTestsMode(e.target.getAttribute('value'))
** const addtoproduction = () => {
console.log(unitTestsData)
}**
return (
<PageWrapper title='View Unit Tests'>
<div className='Main UnitTestsAutoCompleteWrapper' style={{display: 'flex', justifyContent: 'center', alignItems: 'center'}}>
<div style={{width: '30%', marginRight: '10px'}}>
<DropdownSingle
options={['Object', 'Database']}
value='Object'
name='Search unit tests'
onChange={onChangeUnitTestsMode}
/>
</div>
{unitTestsMode === 'Object' ?
<div>
<AutoCompleteSingle
name='Table'
label='Table'
options={autoComplete.objectsAutoComplete}
onChange={autoComplete.onObjectAutoCompleteFieldUpdate}
onSelect={onObjectSelect}
uniqueIdentifier={0}
initialValue={targetObject}
/>
</div> :
<div>
<AutoCompleteSingle
name='Database'
label='Database'
options={autoComplete.databasesAutoComplete}
onChange={autoComplete.onDatabaseAutoCompleteFieldUpdate}
onSelect={onDatabaseSelect}
uniqueIdentifier={1}
/>
<div>
<Button onClick={addtoproduction} />
</div>
</div>
}
</div>
{!unitTestsData ? null :
!unitTestsData.length ?
<div>
This table has no associated unit tests
<div style={{marginTop: '20px'}}>
<Button
text='New Test Case'
icon={<AddIcon className='ButtonIcon' />}
onClick={onNewTestCaseClick}
className='Button Dark Main'
style={{width: '200px'}}
/>
</div>
</div>
:
unitTestsMode === 'Object' ?
<UnitTestsObjectView unitTestsData={unitTestsData}/>
: <UnitTestsDatabaseView unitTestsData={unitTestsData} />
}
{!targetObject ? null :
<div style={{position: 'fixed', bottom: '80px', right: '40px'}}>
<Button
text='New Test Case'
icon={<AddIcon className='ButtonIcon' />}
onClick={onNewTestCaseClick}
className='Button Dark Main'
/>
</div>
}
</PageWrapper>
)
}
export default withRouter(unitTests)

Resources