Formik odd (.value) need when updating formik.values - node.js

I manage a list of related elements in my form with a MUIDataTable(encapsulated here as CrudList) and a MUI Autocomplete.
I managed to add new elements through the autocomplete components onChange and to remove an element from a button using almost the same code. But I need to add .value on the second case Or it doesn't re-render.
What I'm doing wrong?
function RelatedModels({name, value ,model, tittle, columns, optionsSelector, onChange, ...fc}) {
const formik = useFormikContext();
const options = useSelector(createSelector(optionsSelector,
elements=> elements.filter(item => ! value.some(s=> item.idx === s.idx)))
);
const buttons = [
quickButton(
idx => () => {
const a =fc;
debugger;
//THIS NOT RE ENDER
formik.values[name]= value.filter(elem => idx !== elem.idx);
formik.setFieldTouched(name, true, false);
}
, 'Eliminar', <Delete/>)
];
return (
<Paper className="formPanel">
<h1>{tittle}</h1>
<Autocomplete
options={options}
onChange={(o, newElement)=> {
// THIS RE RENDER THE COMPONENT
formik.values[name].value = value.push(newElement);
formik.setFieldTouched(name, true, false);
}}
renderOption={ (option, state) =>
<span>{option.name}</span>
}
renderInput={params =>(
<MuiTextField {...params} label="Select to add" margin="normal" fullWidth/>)
}
/>
<CrudList Model={model} columns={columns.concat(buttons)} elements={value} buttons/>
</Paper> );
}
I include the component in the Formik as Follows
<Field as={RelatedModels}
name="accessories" model={Accessory} optionsSelector={availableAccessories}
tittle="Selecciona accesorio a aƱadir"
columns={accessoriesColumns}
/>

Related

Getting and Inserting array of objects from checkboxes ReactJS

I am having a trouble where an array of Objects are returning [Object object]. What could be the missing fix to get the value of product from the mapped targeted values.
this is my sample array.
const product = [{food:'BREAD',price: 6}]
this is where I map the values and get the targeted value.
<Form >
{product.map((item, index) => (
<div key={index} className="mb-3">
<Form.Check
input value={[item]}
id={[item.food]}
type="checkbox"
label={`${item.food}`}
onClick={handleChangeCheckbox('PRODUCTS')}
/>
</div>
))}
</Form>
this handles the e.target.value from checked checkboxes.
const handleChangeCheckbox = input => event => {
var value = event.target.value;
var isChecked = event.target.checked;
setChecked(current =>
current.map(obj => {
if (obj.option === input) {
if(isChecked){
return {...obj, chosen: [...obj.chosen, value ] };
}else{
var newArr = obj.chosen;
var index = newArr.indexOf(event.target.value);
newArr.splice(index, 1); // 2nd parameter means remove one item only
return {...obj, chosen: newArr};
}
}
return obj;
}),
);
console.log(checked);
}
finally, this is where I am having problems. Chosen is returning [Object object]console.log(checked).
const [checked, setChecked] = useState([
{ option: 'PRODUCTS',
chosen: [],
}
]);
What do I insert inside chosen:[] to read the following arrays. Im expecting to see
0:
food: 'bread'
price: '6'
Thank you so much for helping me!
Html input value prop is a string, and it's change event target value is also string.
Here you are passing an object to the value prop, which will be stringified as [Object object].
Instead, update your change handler to take item instead of event.
const handleChangeCheckbox = (input) => (value) => {
setChecked((current) => {
// Value is checked if it exists in the current chosen array
const isChecked = current.chosen.find((item) => item.food === value.food) !== undefined;
// Remove it from state
if (isChecked) {
return {
...current,
chosen: current.chosen.filter((item) => item.food === value.food),
};
}
// Add it to state
return {
...current,
chosen: [...current, value],
};
});
};
Then update your input element onChange handler, to call your handler with the item itself, instead of the event.
onClick={() => handleChangeCheckbox('PRODUCTS', item)}
I don't know what the props for your component Form.Check are. But, I would expect an input type="checkbox" to have a checked prop.
A checkbox is checked if the item is in the chosen state array.
<Form>
{product.map((item, index) => (
<div key={item.food} className="mb-3">
<Form.Check
type="checkbox"
id={item.food}
label={item.food}
checked={checked.chosen.find((chosen) => chosen.food === item.food) !== undefined}
onClick={() => handleChangeCheckbox('PRODUCTS', item)}
/>
</div>
))}
</Form>
Hmm, don't you need to inline your handleChangeCheckbox function? As otherwise it's just getting executed. So instead onClick={handleChangeCheckbox('PRODUCTS')} do onClick={(event) => handleChangeCheckbox('PRODUCTS', event)}.
Then your handleChangeCheckbox function will start handleChangeCheckbox = (input, event) => {...}

Using Material UI's Autocomplete with Formik Field to display different value with formik state

I am trying to use Material UI's Autocomplete with Formik Field custom component. Here is a custom Autocomplete component I wrote to use with Formik Field.
import { getIn } from 'formik';
import { Autocomplete, TextField } from '#mui/material';
export const CustomAutocomplete = ({
field,
form,
textFieldProps,
...props
}) => {
const errorText =
getIn(form.touched, field.name) && getIn(form.errors, field.name);
return (
<Autocomplete
{...props}
{...field}
onChange={(_, value) => form.setFieldValue(field.name, value)}
onBlur={() => form.setTouched({ [field.name]: true })}
renderInput={(props) => (
<TextField
{...props}
{...textFieldProps}
helperText={errorText}
error={errorText}
/>
)}
/>
);
};
Here is how components called
<Field
options={users}
label='Receiver'
name='receiver'
component={CustomAutocomplete}
textFieldProps={{
fullWidth: true,
margin: 'normal',
variant: 'outlined',
}}
/>
Now what I intend to do is: If I have an object like:
users=[{_id:"62b73809c2ca370bcbb0715b",name:"James"},{_id:"62b739c8c2ca370bcbb07186",name:"David"},
{_id:"62b739c8c2ca3738dbb07101",name:"Steven"}]
I want the Autocomplete dropdown to show options James, David and Steven. But I want the Formik state to save _id respectively once selected from the dropdown because I need to send that _id back to the backend. Currently, the entire object gets saved.
How can this be done?

React, update component after async function set

I want to add data and see in below, and also when I start app, I want see added records. But I can see it, when I'm try to writing something in the fields.
The thing is, the function that updates the static list is asynchronous. This function retrieves data from the database, but before assigning it to a variable, the page has been rendered. There is some way to wait for this variable or update information other way than when you try to type it in the fields. This is before the form is approved.
[![enter image description here][1]][1]
class AddAdvertisment extends React.Component <any, any> {
private advertisment;
constructor(props, state:IAdvertisment){
super(props);
this.onButtonClick = this.onButtonClick.bind(this);
this.state = state;
this.advertisment = new Advertisement(props);
}
onButtonClick(){
this.advertisment.add(this.getAmount(), this.state.name, this.state.description, this.state.date);
this.setState(state => ({ showRecords: true }));
}
updateName(evt){
this.setState(state => ({ name: evt.target.value }));
}
....
render() {
return (<React.Fragment>
<div className={styles.form}>
<section className={styles.section}>
<input id="name" onChange={this.updateName.bind(this)} ></input>
<input id="description" onChange={this.updateDescription.bind(this)} ></input>
<input type="date" id="date" onChange={this.updateDate.bind(this)} ></input>
<button className={styles.action_button} onClick={this.onButtonClick.bind(this)}>Add</button>
</section>
</div>
{<ShowAdvertismentList/>}
</React.Fragment>
);
}
class ShowAdvertismentList extends React.Component <any, any>{
render(){
let listItems;
let array = Advertisement.ad
if(array !== undefined){
listItems = array.map((item) =>
<React.Fragment>
<div className={styles.record}>
<p key={item.id+"a"} >Advertisment name is: {item.name}</p>
<p key={item.id+"b"} >Description: {item.description}</p>
<p key={item.id+"c"} >Date: {item.date}</p>
</div>
</React.Fragment>
);
}
return <div className={styles.adv_show}>{listItems}</div>;
class Advertisement extends React.Component {
public static ad:[IAdvertisment];
constructor(props){
super(props);
if(!Advertisement.ad){
this.select_from_db();
}
}
....
select_from_db = async () => {
const res = await fetch('http://localhost:8000/select');
const odp = await res.json();
if(odp !== "brak danych")
odp.forEach(element => {
if(Advertisement.ad){
Advertisement.ad.push(element);
}
else{
Advertisement.ad = [element];
I try to create function and child like:
function Select_from_db(){
const[items, setItems] = useState();
useEffect(() => {
fetch('http://localhost:8000/select')
.then(res => res.json())
.then(data => setItems(data));
}, []);
return <div className={styles.adv_show}>{items && <Child items={items}/>}
</div>;
}
function Child({items}){
return(
<>
{items.map(item => ( ...
))}
</>
And is working good in first moment, but if I want add item to db I must refresh page to see it on a list below.
I use is instead ShowAdvertismentList in render function. Elements be added to db but not showing below. In next click is this same, until refresh page.
And in my opinio better use a list, becouse I musn't want to conect to database every time to download all records.
[1]: https://i.stack.imgur.com/IYSNU.gif
I now recipe. I must change state on componentDidMount in AddAdvertisment class.
async componentDidMount(){
let z = await setTimeout(() => {
this.setState(state => ({ loaded: true}));
}, 1000);
}
render() {
return (<React.Fragment >
(...)
{this.state.loaded ? <ShowAdvertismentList /> : <Loading/>}
</React.Fragment>
);
}

Nextjs how to not unmount previous page when going to next page (to keep state)

we are using Nextjs in our web app.
We want to keep stack of pages where users visit to keep state of component on back navigation.
How should we do that?
I have tried https://github.com/exogen/next-modal-pages, but it calls getInitialProps of previous pages again on back.
Here's my solution with a custom _app.js
import React, { useRef, useEffect, memo } from 'react'
import { useRouter } from 'next/router'
const ROUTES_TO_RETAIN = ['/dashboard', '/top', '/recent', 'my-posts']
const App = ({ Component, pageProps }) => {
const router = useRouter()
const retainedComponents = useRef({})
const isRetainableRoute = ROUTES_TO_RETAIN.includes(router.asPath)
// Add Component to retainedComponents if we haven't got it already
if (isRetainableRoute && !retainedComponents.current[router.asPath]) {
const MemoComponent = memo(Component)
retainedComponents.current[router.asPath] = {
component: <MemoComponent {...pageProps} />,
scrollPos: 0
}
}
// Save the scroll position of current page before leaving
const handleRouteChangeStart = url => {
if (isRetainableRoute) {
retainedComponents.current[router.asPath].scrollPos = window.scrollY
}
}
// Save scroll position - requires an up-to-date router.asPath
useEffect(() => {
router.events.on('routeChangeStart', handleRouteChangeStart)
return () => {
router.events.off('routeChangeStart', handleRouteChangeStart)
}
}, [router.asPath])
// Scroll to the saved position when we load a retained component
useEffect(() => {
if (isRetainableRoute) {
window.scrollTo(0, retainedComponents.current[router.asPath].scrollPos)
}
}, [Component, pageProps])
return (
<div>
<div style={{ display: isRetainableRoute ? 'block' : 'none' }}>
{Object.entries(retainedComponents.current).map(([path, c]) => (
<div
key={path}
style={{ display: router.asPath === path ? 'block' : 'none' }}
>
{c.component}
</div>
))}
</div>
{!isRetainableRoute && <Component {...pageProps} />}
</div>
)
}
export default App
Gist - https://gist.github.com/GusRuss89/df05ea25310043fc38a5e2ba3cb0c016
You can't "save the state of the page by not un-mounting it" but you can save the state of your app in _app.js file, and the rebuild the previous page from it.
Check the redux example from next's repo.

React: Stumped with how to select table rows with a checkbox and send the values to the server side with node.js

Hello I am working on a process with React that will allow users to select a row or rows from a table by selecting check-boxes.
I need assistance with how once a row is checked, how can I store this information but at the same time if the row is unchecked I would also want to update the state.
Than when the user selects the submit button it will send the array object to the server side.
I have an empty array in my state and in the method that handles selecting a checkbox I am attempting to push the data to the array and than send the array with a form.
It appears as if the array is not being updated or I am missing something?
class TestStatus extends Component {
constructor (props) {
super(props)
this.state = {
selected: []
}
handleCheckChildeElement = (event) => {
var data = this.global.data;
data.forEach(data => {
if(data.testid === event.target.value) {
data.isChecked = event.target.checked
if(event.target.checked === true) {
this.setState({ selected: [ ...this.state.selected, data]
});
}
console.log(this.state.selected);
}
});
this.setGlobal({ data });
}
handleSubmit(event) {
event.preventDefault();
axios.post('http://localhost:5000/api/advanced_cleanup',
this.state.selected)
.then((res) => {
console.log("Sending tests");
}).catch(event => console.log(event));
}
render() {
return(
<div>
<table>
<AdvancedRows checked={this.handleCheckChildeElement}
handleCheckChildeElement={this.handleCheckChildeElement}/>
</table>
<form className="ui form" onSubmit={this.handleSubmit}>
<button
className="ui basic blue button" type="submit"
style={{ marginBottom: '5em' }}>
Submit
</button>
</form>
</div>
);
}
}
I expect to be able to select a checkbox or multiple and update the state array based on what is checked and than send that data to the server side.
After some additional research online I found the correct way with react to update the state array and than update it upon unchecking a check box.
If the targeted row is checked it will pass that rows object into the state array otherwise if the check box of the row is unchecked it will iterate over the state array and filter out the item that was unchecked.
This is the guide I used to assist me. https://scriptverse.academy/tutorials/reactjs-update-array-state.html
if(event.target.checked === true) {
this.setState({ selected: [...this.state.selected, data ] });
} else {
let remove = this.state.selected.map(function(item) {
return item.testid}).indexOf(event.target.value);
this.setState({ selected: this.state.selected.filter((_, i) => i !== remove) }); }
Expanding on my comment above.
handleCheckChildeElement = (event) => {
var data = this.global.data;
// create an empty array so that each click will clean/update your state
var checkedData = [];
data.forEach(data => {
if(data.testid === event.target.value) {
data.isChecked = event.target.checked
if(event.target.checked === true) {
// instead of setting your state here, push to your array
checkedData.push(data);
}
console.log(checkedData);
}
});
// setState with updated checked values
this.setState({selected: checkedData});
this.setGlobal({ data });
}

Resources