'this' issue while mapping a List with button reactjs - javascript

I'm mapping results from a GET request. The mapping works fine. I get each User picture, name and even the correct label on the button. But when I console.log on the button, I want to get info of the user. When I click on the button, it gives user_id of all users not the particular one.
{filteredEmails.map(user => {
return (
<List>
<div className="mail" key={user.user_id}>
<ListItem
key={user.user_id}
disabled={true}
leftAvatar={
<Avatar size={80} src={user.picture} />
}
rightIconButton={
<RaisedButton
label={user.name}
primary={true}
key={user.user_id}
onTouchTap={console.log(user.user_id)}
style={style} />
}
>
<div className="searchContent" key={user.user_id}>
<div className="subject">{user.name}</div>
<br></br>
<div className="from">{user.email}</div>
<br></br>
<div className="subject">{user.identities[0].provider}</div>
</div>
</ListItem>
</div>
</List>
);
})}
Mapping users picture
On the above picture, I want to press the button and get user_id or name of the user. I can't seem to do that.
My guess is problem with this context, but I'm unable to fix it so far.

You need to pass a function to onTouchTap event, and bind the value of user_id to that, by this way whenever user click on button that specific user_id will get passed to that function, then do the console.log() inside that it will print the proper value.
Like this:
onTouchTap={() => this._handleClick(user.user_id)}
Define the _handleClick function like this:
_handleClick(user_id){
console.log(user.user_id)
}
You also need to define the unique key to each List item inside map otherwise it will throw warning, user_id will have the unique values you can use that also.
Write the map like this:
{filteredEmails.map(user => {
return (
<List key={user.user_id}>
<div className="mail">
<ListItem
key={user.user_id}
disabled={true}
leftAvatar={
<Avatar size={80} src={user.picture} />
}
rightIconButton={<RaisedButton
label={user.name}
primary={true}
key={user.user_id}
onTouchTap={onTouchTap={() => this._handleClick(user.user_id)}}
style={style} />
}
>
<div className="searchContent" key={user.user_id}>
<div className="subject">{user.name}</div>
<br></br>
<div className="from">{user.email}</div>
<br></br>
<div className="subject">{user.identities[0].provider}</div>
</div>
</ListItem>
</div>
</List>
);
})}

Related

Updating default values of a React-Hook-Form, does not populate filelds with useFieldArray

This problem has consumed a lot of time trying to figure out what is wrong. I have a React-Hook-Form (v6.14.1) that needs to populate dynamic data, based on the component state.
On the initial load, everything works fine. If I change the state all updated data are displaying fine, except the dynamic data.
Here is a codesandbox link. If it does not render due to a library error, just hit the preview refresh button.
The goal is that the WAN 1 tab, on initial load displays the dynamic fields (WAN 1 VLAN-1) and WAN2 does not have dynamic fields to display. Hitting the Update Config button, WAN1 should not have dynamic fields to display and WAN2 should display one (WAN 2 VLAN-1). The problem is that WAN2 does not display it.
I have searched for similar questions, but all of them were about the values of the populated fields and not about displaying the fields themselves. I have used the reset method of react-hook-form and the defaltValue for each dynamic field as react-hook-form documentation suggests.
On App.js I have the state, a button that updates the state, and the Form component which has the state as property.
const [configdata, setConfigdata] = useState(config);
return (
<div className="App">
<UpdateConfig onClick={() => setConfigdata(configUpdated)} />
<Form
formData={configdata}
handleFormData={(data) => console.log(data)}
/>
</div>
);
}
On Form.js there is a Rect-hook-form FormProvider and the WanFields component that dynamically populates form fields.
<FormProvider {...methods}>
<form
onSubmit={methods.handleSubmit((data) =>
props.handleFormData(data)
)}
>
<Tab.Content>
{props.formData?.intfs?.length &&
props.formData?.intfs.map((intf, index) => (
<Tab.Pane key={index} eventKey={`wan${index}-tab`}>
<WanFields
key={`wan${index}-fields`}
intfNo={index}
portTypeOptions={props.portTypeOptions}
data={intf}
/>
</Tab.Pane>
))}
</Tab.Content>
</form>
</FormProvider>
Every time the props.formData update, there is a useEffect that reset the forms' default data.
const methods = useForm({ defaultValues: props.formData });
useEffect(() => {
methods.reset(props.formData);
}, [props.formData]);
In WanFields.js, there are all the form fields, and the useFieldArray method, that will populate the dynamic fields based on the forms' default values and a watch field value (watchIntfType ).
const methods = useFormContext();
const { errors, control, watch, register } = methods;
const { fields, append, remove } = useFieldArray({
control,
keyName: "fieldid",
name: `intfs[${intfNo}].subIntfs`
});
const watchIntfStatus = watch(`intfs[${intfNo}].enabledStatus`);
const watchIntfType = watch(`intfs[${intfNo}].enabled`);
Dynamic fields are populated as follows
{watchIntfType?.value >= "2" && (
<>
<div className="form-group">
<div className="btn btn-success" onClick={append}>
Add
</div>
</div>
<div id={`accordion-${intfNo}`}>
<Accordion>
{console.log("FIELDS", fields)}
// This is where the problem starts. fields are empty after updating data
{fields.map((field, index) => {
return (
<Card key={field.fieldid}>
<Accordion.Toggle
as={Card.Header}
variant="link"
eventKey={`${index}`}
style={{ cursor: "pointer" }}
>
<h4>
WAN {parseInt(intfNo) + 1}{" "}
<span style={{ margin: "0px 5px" }}>
<i className="fas fa-angle-right"></i>
</span>{" "}
VLAN-{index + 1}
</h4>
<div className="card-header-action">
<button
type="button"
className="btn btn-danger"
onClick={() => remove(index)}
>
Remove
</button>
</div>
</Accordion.Toggle>
<Accordion.Collapse eventKey={`${index}`}>
<Card.Body>
<div className="form-row">
<div className="form-group col-12 col-md-6">
<label>IP</label>
<input
type="text"
className="form-control"
name={`intfs[${intfNo}].subIntfs[${index}].ipAddress`}
defaultValue={field?.ipAddress}
ref={register()}
/>
</div>
<div className="form-group col-12 col-md-6">
<label>Subnet</label>
<input
type="number"
className="form-control"
min="0"
max="30"
name={`intfs[${intfNo}].subIntfs[${index}].subnet`}
defaultValue={field?.subnet}
ref={register()}
/>
</div>
</div>
</Card.Body>
</Accordion.Collapse>
</Card>
);
})}
</Accordion>
</div>
</>
)}
The problem is that when the state updates, form default values are updated, but the method useFieldArray attribute fields are not updated and stay as an empty array. I really cannot understand, what I am doing wrong. Any help will be much appreciated.
I don't know if is a correct method but i have resolv this probleme with method reset in a useEffect.
https://react-hook-form.com/api/useform/reset
defaultValues:
{
acvDesignOffice: generateRSEnv.acvDesignOffice,
earthQuakeZone: generateRSEnv.earthQuakeZone,
buildings: generateRSEnv.buildings,
},
useEffect(() => {
reset({
acvDesignOffice: generateRSEnv.acvDesignOffice,
earthQuakeZone: generateRSEnv.earthQuakeZone,
buildings: generateRSEnv.buildings,
});
}, [generateRSEnv]);

How can i get values of input sent to api on ok button of ant design modal?

I'm trying to implement a kind of table, which has an add button that opens a modal.
Inside the modal, I have the inputs that I want to update in the table, but using the ant design modal it has an ok button and a cancel button. How do I make the path to get the values? I'm having trouble understanding/writing this syntax. Can someone help me?
on the "onOk", i don't know how to write the function, tried creating a onSubmit(values) and console.log it but it doesn't show
Here's the code
function showModal(nome,colunas) {
setFormDisplay("");
setModal(!modal);
setFormName(nome);
setFormColumns(colunas);
}
function cancelModal() {
setFormDisplay("none");
setModal(false);
setFormName("");
setFormColumns([]);
}
<>
<div className="">
<CardsHost posts={nomes} />
</div>
<Modal
visible={modal}
onOk={}
title="Novo Prontuário"
onCancel={cancelModal}
style={{display:`${formDisplay}`}}
width={1000}
>
{formColumns.map((column,index) => (
<>
<div className="labelll">
<label key={`label-${index}`}>{column.title}</label>
{(column.key !=='proc' && column.key !== 'meds' )
? <Input key={`input-${index}`} style={{ width: "61.3%" }} />
: (column.key == 'proc' ) ? <div className="pesquisa-input"><Demo /></div>
: <div className="pesquisa-input"><PageComponentMeds /> </div>
}
</div>
{/*<div className="labelll">
<label> Data de Atendimento</label>
<DatePicker style={{ width: "61.3%" }} />
</div>
<div className="labelll">
<label> Nota </label>
<TextArea style={{ width: "61.3%" }} />
</div> */}
</>
))}
</Modal>
</>
);
}
The easiest way would be use to add a state variable for both of your input values. You can update them using the onChange callback provided by the antd components. On submit you use the state values to make your api call.
Make sure that you reset the variables on cancel and after a successful call.

react-beautiful-dnd: Prevent the rest of draggable items from reordering / moving up and replacing the item being dragged

In a single Droppable, I have mapped out an array of items, each as its own Draggable. My code is working properly and is doing what it's supposed to (i.e., I can move each Draggable individually). I'm just wondering if there's a way to stop the rest of my Draggables from "moving up" or replacing the empty spot the item-that's-being-dragged leaves behind.
Here's my code (DragDropContext is in another component, so it's not shown here):
<Droppable
droppableId='itemDroppable'
type='acceptsItems'
isDropDisabled={true} >
{(provided) =>
<div ref={provided.innerRef}>
{this.props.items.map((item, index) =>
<Draggable
key={item.id}
draggableId={item.name}
index={index} >
{provided =>
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps} >
{item.icon}
</div>
}
</Draggable>
)}
{provided.placeholder}
</div>
}
</Droppable>
And here's a video of what's happening:
As you can see, the rest of the following icons move up and replace the empty spot left by the item that's being dragged out of the Droppable. Is there a way to stop this? Ideally, I'd like to have the empty spot stay empty and not have the rest of the proceeding icons move up and fill up.
Thank you in advance!
I don't have a direct solution but I have an idea that may work around this problem.
when you are displaying the items using Array.map() function
first: suppose we add some additional information to the object to track if it's deleted.
add a condition so It renders the element if the flag is true.
Otherwise, a hidden element will be displayed.
note: I not sure but I think you can use provide.placeholder
for example:
{(provided) => (
<div ref={provided.innerRef}>
{this.props.items.map((item, index) => {
if (item.id !== 0) {
return (
<Draggable
key={item.id}
draggableId={item.name}
index={index}
>
{(provided) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
{item.icon}
</div>
)}
</Draggable>
);
} else
return (
<div style={{ width: '20px', height: '20px' }} /> //display hidden undraggable hidden element or try provided.placeholder
);
})}
{provided.placeholder}
</div>
)}
second: when you implement the deleting function inside onDragEnd() we can change the flag of that element.

Open a default input in form tag

I have the next application:
https://codesandbox.io/s/uwmig?file=/index.js,
There users can add as many fields as they want and add images for each generated input, clicking on add button.
What I want to achieve is to set a default open input in my application and to get something like this:
Now the default section is open by doing:
const firstRowOpen = { name: 0, key: 0, isListField: true, fieldKey: 0 };
and:
{fields.concat(firstRowOpen).map((field, index)... //here i map already concatinated fields and this is why appears first default block
Code:
<Form.List name={[fieldKey, "inner"]}>
{(fields, { add, remove }) => {
return (
<div>
{fields.concat(firstRowOpen).map((field, index) =>
!fieldsOnEdit.includes(index) ? (
<Space
key={field.key}
style={{ display: "flex", marginBottom: 8 }}
align="start"
>
<Demo
idx={index}
upload={upload}
setUpload={setUpload}
field={field}
/>
<Form.Item>
<Button
type="primary"
htmlType="submit"
key="submit"
onClick={() => toggleSmall(index)}
>
Toggle first row
</Button>
</Form.Item>
</Space>
) : (
<Edit value={values} mode={toggleSmall} keyForm={index} />
)
)}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
block
>
<PlusOutlined /> Add field to inner
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
Issue: when I add an image clicking on add button, and after that click on toggle first row button, appears another buttons bellow.
Question: Why this issue appears? and how to solve the issue?
demo: https://codesandbox.io/s/wonderful-ives-o81ue?file=/SubForm.js:767-2195
If I understand right you need to use initialValues
Here is an updated example of your code
https://codesandbox.io/s/compassionate-fermi-4oedx?file=/SubForm.js
...
<Form
name="dynamic_form_item"
{...formItemLayoutWithOutLabel}
onFinish={onFinish}
initialValues={{ names: [""] }}
>
...

Remove specific array element, not just last one with .pop - Redux-form

https://codesandbox.io/s/qzm5q6xvx4
I've created the above codesandbox. I am using redux-form ([https://redux-form.com]) that where you can add and remove fields to populate the form using .push and .pop 2.
The problem with using .pop it only takes off the last array element, I would like the option for each .push created element to have its own "remove" button, therefore not simply taking the last item off the array.
I assume I would need to assign the .pop to look at the matching react .map element somehow?
const renderForm = ({ fields, label }) => (
<div>
<div
variant="fab"
color="primary"
className="jr-fab-btn"
aria-label="add"
onClick={() => fields.push()}
>
ADD
</div>
<div
variant="fab"
color="primary"
className="jr-fab-btn"
aria-label="add"
onClick={() => fields.pop()}
>
REMOVE
</div>
{fields.map((newIntel, index) => {
console.log("newIntel", newIntel, index);
return (
<Field
name={newIntel}
key={index}
label={label}
placeholder={label}
component={renderTextField}
placeholder={label}
label={label}
/>
);
})}
</div>
);
Any ideas would be welcome.
If you will look into the fields which is a prop to your renderForm, it contains a method remove to remove a specific element. Just pass it the index.
below is modified code-block of your component. I have made it class-component:
class renderForm extends React.Component {
render(){
let {fields, label} = this.props;
const removeName = (index) => {
fields = fields.remove(index);
}
return(
<div>
<div
variant="fab"
color="primary"
className="jr-fab-btn"
aria-label="add"
onClick={() => fields.push()}
>
ADD
</div>
{fields.map((newIntel, index) => {
console.log("newIntel", newIntel, index);
return (
<div>
<Field
name={newIntel}
key={index}
label={label}
placeholder={label}
component={renderTextField}
/>
<p
variant="fab"
color="primary"
style={{'cursor': 'pointer'}}
className="jr-fab-btn"
aria-label="add"
onClick={() => removeName(index)}
>
REMOVE
</p>
</div>
);
})}
</div>
)
}}
Hope, you can understand the code-block easily. Just paste the above code in place of your renderForm component. It will work like magic. :p

Categories

Resources