I am trying to render a list using a child component and it ends up not rendering anything.
Snippet in PropertyBar component:
updateSelectedCell() {
if (graph.selectedCell != null) {
this.setState({
selectedCell: graph.selectedCell,
cellProperties: Array.from(graph.selectedCell.value.attributes)
});
}
}
renderPropertyList() {
this.state.cellProperties.map(key => {
<PropertyBarItem name={key.nodeName} value={key.nodeValue} />
})
}
render() {
if (this.state.selectedCell != null) {
return (
<div>
<Button variant="contained" color="primary" fullWidth={true} onClick={() => this.updateSelectedCell()}> View properties </Button>
<List>
{this.renderPropertyList}
</List>
</div>
)
}
return (
<div>
<Button variant="contained" color="primary" fullWidth={true} onClick={() => this.updateSelectedCell()}> View properties </Button>
<List>
<p>Click on a cell to view its properties.</p>
</List>
</div>
)
}
When a user clicks on the button, it updates the state with an array then should render a list of PropertyBarItem with the keys inside the array. It ends up not displaying anything, but doesn't crash. Testing using a simple p tag and it also does not render anything:
renderPropertyList() {
this.state.cellProperties.map(key => {
<p> {key} </p>
})
}
You are not invoking the renderPropertyList method. Add () to invoke it.
<div>
<Button
variant="contained"
color="primary"
fullWidth={true}
onClick={() => this.updateSelectedCell()}
>
View properties
</Button>
<List>{this.renderPropertyList()}</List>
</div>
You must also return the result from renderPropertyList and return the JSX from the function given to map as well.
renderPropertyList() {
return this.state.cellProperties.map(key => {
return <PropertyBarItem name={key.nodeName} value={key.nodeValue} />
})
}
You're missing the return statement:
renderPropertyList() {
return this.state.cellProperties.map(key => {
<PropertyBarItem name={key.nodeName} value={key.nodeValue} />
})
}
Also, be sure to invoke the method:
if (this.state.selectedCell != null) {
return (
<div>
<Button variant="contained" color="primary" fullWidth={true} onClick={() => this.updateSelectedCell()}> View properties </Button>
<List>
{this.renderPropertyList()}
</List>
</div>
)
}
Related
i am trying to use useFieldsArray from react-hook-form, but when I click on the button to delete, instead of deleting the correct element it always deletes the last one. can someone more experienced give me some advice?
I don't understand what I'm doing wrong.
Thanks in advance.
export const isThisAFieldsArrayContext = React.createContext(false)
export default function FieldsArray({
control,
formNamePrefix,
register,
inputsToRender,
addNewDisabled = false,
watchConfig,
...props
}) {
const classes = useStyles()
const { append, remove } = useFieldArray({
control,
shouldUnregister: false,
name: formNamePrefix,
})
const additionalProps = useWatchForAdditionalProps(watchConfig)
function handleRemove(index) {
remove(index)
}
return (
<isThisAFieldsArrayContext.Provider value={true}>
<Grid item xs={12}>
<List>
{control.getValues(formNamePrefix)?.map((item, index) => {
console.log({ item })
return (
<ListItem key={index} className={classes.listItem}>
<DynamicFormFields
{...props}
register={register}
fieldsKey={`${formNamePrefix}.${index}`}
formFields={inputsToRender}
control={control}
// defaultValue={item.defaultValue}
/>
<DeleteButton
variant="contained"
onClick={() => handleRemove(index)}
aria-label="delete"
className={classes.deleteButton}
>
<DeleteIcon color="secondary" fontSize="small" />
</DeleteButton>
</ListItem>
)
})}
</List>
<Button
variant="contained"
onClick={() => append({})}
aria-label="add"
color="secondary"
className={classes.addButton}
disabled={addNewDisabled || additionalProps.disabled}
>
<AddIcon color="primary" />
</Button>
</Grid>
</isThisAFieldsArrayContext.Provider>
)
useFieldArray returns fields
const { fields, ... } = useFieldArray(...)
Use it to map
fields.map((item, index) => {
// there is an 'id' in item. Use it for key!
return (
<ListItem key={item.id}>
...
<button onClick={() => handleRemove(index)}>Delete</button>
...
</ListItem>
)
})
The value of 'formNamePrefix' has to be an array of objects.
Example:
useForm({
defaultValues: {
[formNamePrefix]: [{name: 'name1'}, {name: 'name2'}, ...]
}
})
ERRORCODE:
index.js:1 Warning: Instance created by `useForm` is not connected to any Form element. Forget to pass `form` prop?
I am using React with Antd and I open a modal on a button click, the button passes some data which I am capturing via "e" and render them onto the modal.
The problem is whenever the modal is closed and opened again, the contents inside do not re render, it uses the previous event data. I've figured it is because I am not using the Form properly.
I will post the code below please let me know where I have gone wrong.
import react, antd ...etc
function App () => {
const sendeventdata(e) => {
const EventContent = () => (
<div>
<Form
form={form}
labelAlign="left"
layout="vertical"
initialValues={{
datePicker: moment(event.start),
timeRangePicker: [moment(event.start), moment(event.end)],
}}
>
<Form.Item name="datePicker" label={l.availability.date}>
<DatePicker className="w-100" format={preferredDateFormat} onChange={setValueDate} />
</Form.Item>
<Form.Item name="timeRangePicker" label={l.availability.StartToEnd}>
<TimePicker.RangePicker className="w-100" format="HH:mm" />
</Form.Item>
<span className="d-flex justify-content-end">
<Button
className="ml-1 mr-1"
onClick={() => {
form
.validateFields()
.then((values) => {
onUpdate(values)
form.resetFields()
})
.catch((info) => {
console.log('Validate Failed:', info)
})
}}
>
{l.availability.updateBtn}
</Button>
<Button danger className="ml-1 mr-1" onClick={() => handleDelete()}>
Delete
</Button>
</span>
</Form>
</div>
)
}
const MyModal = () => {
const { title, visible, content } = e
return (
<Modal
onCancel={handleModalClose}
title={title}
visible={visible}
footer={null}
form={form}
destroyOnClose
>
{content}
</Modal>
)
}
return <div><MyModal /><Button onClick{sendeventdata}></Button></div>
}
When I click the login or register button in the following react code, the renderView() function is not rendering the Login(or the Register) component on the page.
When I log the value of e.currentTarget.value in the console, the correct values are getting logged when the button is clicked.
How do I fix this?
I want the corresponding components to be displayed when I click the Login or Register Buttons
const AppHomePage = () => {
const classes = useStyles();
const [renderVal, setRenderVal] = useState(0);
const renderView = () => {
switch(renderVal){
case 0:
return <Page />
case 1:
return <Register />
case 2:
return <Login />
default:
return <Page />
}
}
const ButtonChange = (e) => {
setRenderVal(e.currentTarget.value);
}
return(
<div>
<AppBar className={classes.root} position='static' >
<Toolbar className={classes.appbar}>
<Button className={classes.buttons} value={1} variant="contained" color="primary" onClick={ButtonChange}>
Register
</Button>
<Button className={classes.buttons} value={2} variant="contained" color="primary" onClick={ButtonChange}>
Login
</Button>
</Toolbar>
</AppBar>
<div>
{ renderView() }
</div>
<CssBaseline />
</div>
)
}
const Login = () => {
return(
<div>
<p>login</p>
</div>
)
}
const Register = () => {
return(
<div>
register
</div>
)
}
const Page = () => {
return(
<div>
page
</div>
)
}
Pass the value inside onClick function. Use an arrow function and pass the id like 1 or 2. onClick={() => ButtonChange(1)}
<Button className={classes.buttons} variant="contained" color="primary" onClick={() => ButtonChange(1)}>
Register
</Button>
<Button className={classes.buttons} variant="contained" color="primary" onClick={()=>ButtonChange(2)}>
Login
</Button>
Now in ButtonChange function get the value passed in previous step.
const ButtonChange = (id) => {
setRenderVal(id);
}
I fixed it by simply changing the cases from case 1: to case '1': and so on...
How do I hide this Ionic React button 'Submit' on clicking itself?
<IonButton
size="large"
color="primary"
expand="full"
onClick={() => {
showOptionCardDisplay();
}}
>
Submit
</IonButton>
You can use Hooks:useRef to attach the button. Then you can do whatever to it. For example in this case set an attribute like "hidden" to "true".
const btnref = useRef<HTMLIonButtonElement>(null);
...
<IonButton
size="large"
color="primary"
expand="full"
ref={btnref}
onClick={() => { setIsShowBtn(false); btnref.current?.setAttribute("hidden","true");}}
shape="round"
>
Submit
</IonButton>
you can use something like below to hide the button, but I think I use the wrong approach.
const showOptionCardDisplay = (() => {
var x = document.getElementById("myDIV");
if (x.style.display === "none") {
x.style.display = "block";
} else {
x.style.display = "none";
}
});
<div id="myDIV">
<IonButton
size="large"
color="primary"
expand="full"
onClick={() => {
showOptionCardDisplay();
}}>
Submit
</IonButton>
</div>
use flag to hide button.
example:
const isHidden = useState(false);
{
isShowBtn && (
<IonButton
size="large"
color="primary"
expand="full"
onClick={() => { setIsShowBtn(false); })
>
Submit
</IonButton>
)}
While searching I've found how to use ternary operator to change icon of a button, similar approach can be used to hide button
const [playing, setPlaying] = useState(false);
const play = () => {
if (playing == true){
// stop and start again
player.playbackManager.pause();
setPlaying(false);
}else{
// play
player.playbackManager.play();
setPlaying(true);
}
};
return (
<div>
<div className="controller-bar">
<IonButton component="span" onClick={play}>
{playing ? ( <PlayArrow /> ) : ( <Pause/> )}
</IonButton>
</div>
);
}
And here how to hide button
return (
<div>
<div className="controller-bar">
{playing ? (
<IonButton component="span" onClick={play}>
<PlayArrow />
</IonButton>
) : ( null )}
</div>
);
}
I have a modal form with a form that uses Formik. Here are two pictures that show two states of that form that can be toggled with a switch.Initially I fill text into fields which can be added dynamically and stored as an array with .
The second picture shows how I toggled to textarea. There you can also add text with commas that will be turned into an array.
Is there any way to fill data in input fields from the first screen, toggle into textarea and access already inputted data.
I understand formik keeps that state somewhere. But at the moment these fields have a separate state.
Here is my component:
class ModalForm extends React.Component {
constructor(props) {
super(props);
this.state = {
disabled: true,
};
}
onChange = () => {
this.setState({
disabled: !this.state.disabled,
});
};
render() {
var {
visible = false,
onCancel,
onRequest,
submitting,
setSubscriberType,
editing,
subscriptionTypeString,
tested,
selectedGates,
} = this.props;
const { gateId } = selectedGates.length && selectedGates[0];
const handleSubmit = values => {
console.log(values);
onRequest && onRequest({ gateId, ...values });
};
const { disabled } = this.state;
return (
<Modal
footer={null}
closable
title="Список абонентов для выбранного гейта"
visible={visible}
onCancel={onCancel}
onOk={handleSubmit}
destroyOnClose
width="600px"
>
<StyledDescription>
<Switch onChange={this.onChange} />
<StyledLabel>массовый ввод</StyledLabel>
</StyledDescription>
<Formik
initialValues={{ abonents: [''] }}
onSubmit={handleSubmit}
render={({ values, handleChange }) => (
<Form>
{disabled ? (
<FieldArray
name="abonents"
render={arrayHelpers => {
return (
<div>
{values.abonents.map((value, index) => (
<div key={index}>
<MyTextInput
placeholder="Абонент ID"
name={`abonents.${index}`}
value={value}
onChange={handleChange}
/>
<Button
shape="circle"
icon="delete"
onClick={() => {
arrayHelpers.remove(index);
}}
/>
</div>
))}
<Button type="dashed" onClick={() => arrayHelpers.push('')}>
<Icon type="plus" />Добавить абонента
</Button>
</div>
);
}}
/>
) : (
<StyledField
placeholder="Введите ID абонентов через запятую"
name="message"
component="textarea"
/>
)}
<Footer>
<Button type="primary" htmlType="submit">
Запросить
</Button>
</Footer>
</Form>
)}
/>
</Modal>
);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.0.0/umd/react-dom.production.min.js"></script>
Pretty easy, formik stores values inside values.abonents, hence you can use it inside textarea
let { Formik, Form, Field, ErrorMessage, FieldArray } = window.Formik;
function App () {
const [disabled, setDisabled] = React.useState(false) // some boilerplate code
function submit (values) {
console.log('submit', values)
}
return (
<Formik
initialValues={{ abonents: [] }}
onSubmit={submit}
render={({ values, handleChange, setFieldValue }) => (
<Form>
<FieldArray
name='abonents'
render={arrayHelpers => {
if (!disabled) {
return (
<textarea onChange={(e) => {
e.preventDefault()
setFieldValue('abonents', e.target.value.split(', '))
}} value={values.abonents.join(', ')}></textarea>
)
}
return (
<div>
{
values.abonents.map((value, index) => (
<div key={index}>
<input
placeholder='Абонент ID'
name={`abonents.${index}`}
value={value}
onChange={handleChange}
/>
<button onClick={(e) => {
e.preventDefault()
arrayHelpers.remove(index)
}}>
-
</button>
</div>
))
}
<button onClick={(e) => {
e.preventDefault()
arrayHelpers.push('')
}}>
+
</button>
</div>
)
}}
/>
<button type='submit'>Submit</button>
<button onClick={e => {
e.preventDefault()
setDisabled(!disabled)
}}>toggle</button>
</Form>
)}
/>
)
}
ReactDOM.render(<App />, document.querySelector('#root'))
<script src="https://unpkg.com/react#16.9.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.9.0/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/formik/dist/formik.umd.production.js"></script>
<div id='root'></div>
I found a solution. You have got to give the same name to the input and textarea, so whe you add text in input will automatically change text in textarea
<FieldArray
name="abonents"
render={arrayHelpers => {
and
<StyledField
placeholder="Введите ID абонентов через запятую"
name="abonents"
component="textarea"
/>
These two fields have same name so they are rendered interchangeably but have common text inside them