Field Array in React using formik - reactjs

I wanted to follow like a todo list. Something just like in the picture below.
Right now, the problem with my code is that, its working differently.
I wanted only one input that renders a list of divs instead of input fields.
Exactly just like in the picture below:
Pls check my codesandbox here
CLICK HERE
<FieldArray
name="todos"
render={(arrayHelpers) => (
<div>
{formik.values.todos.map((friend, index) => (
<div key={index} style={{ display: "flex", gap: "1rem" }}>
<input
name={`todos[${index}].name`}
value={formik.values.todos[index].name}
onChange={formik.handleChange}
/>
<button
disabled={formik.values.todos?.length === 1}
type="button"
onClick={() => arrayHelpers.remove(index)}
className="deleteButton"
>
Remove
</button>
</div>
))}
<button
type="button"
onClick={() => arrayHelpers.push({ name: "" })}
>
Add
</button>
</div>
)}
/>

Here is a very simple and rude approach - I used Formik my project some time ago and I'm pretty sure it's not perfect but this is exactly what you need.
So some details.
do not render input for every item in your list, you need only render it's value and element with delete handler
you need only one controlled input and it's value is used for name field in your todos. State is cleaned when you click Add button.
you need to render your element only if formik.values.todos.lenght > 0
you need to disable Delete button when formik.values.todos.lenght === 1
Add button should be disabled when newValue.lenght === 0
Piece a cake, right?

Related

State applies to a group of elements instead of individual

The idea is: I have 10 buttons, when I click on one button the recognition starts and the color of this button changes, when the recognition stops the button's color changes back to its initial.
But the problem is, when I click on the button, all buttons change its color and when the recognition stops, all 10 buttons change its color back to the initial one.
I keep the state of recognition (true/false) in useState: const [listening, setListening] = useState(false).
Then I map via array of buttons and check if recognition is listening - true otherwise - false:
<button
onClick={() => {
startRec(phrases.transcript)
}}
>
{!listening ? (
<img src="/recognize_phrase.png" width={40} height={40} />
) : (
<img src="/recognize_phrase_active.png" width={40} height={40} />
)}
</button>
You can take another state that stores the index (taken during the mapping array) of the button clicked, so if the id of a button matches with the state it changes its appearance.
<button
onClick={() => {
setId(id);
startRec(phrases.transcript)
}}
>
{(!listening && (idx != id)) ? (
<img src="/recognize_phrase.png" width={40} height={40} />
) : (
<img src="/recognize_phrase_active.png" width={40} height={40} />
)}
</button>
This ensures that the appearance of the button clicked is only changed.

Autoclose bootstrap 5 Dropdown without toggle in React

I have a search field that shows data as dropdown.item when user is typing. The library is React Bootstrap (bootstrap 5). This is working great. The dropdown is showing. The problem is that the dropdown persist when clicking outside or navigating to a new link. The dropdown is in a header that does not get rerendered with the rest of the page. Using NextJS. Any tips on how to close a dropdown that has no toggle?
<form className="d-none d-sm-inline-block" style={{ zIndex: "1000" }}>
<div className="input-group input-group-navbar">
<input
type="text"
className="form-control"
placeholder="Søk"
aria-label="Search"
onChange={(event) => {
setSearchTerm(event.target.value);
}}
/>
<button className="btn" type="button">
<Icon.Search className="align-middle" />
</button>
</div>
{searchData.length >= 1 && (
<Dropdown style={{ position: "absolute", background: "white" }} autoClose="outside">
{searchData.slice(0, 10).map((element, index) => {
return (
<Link key={element.agressoResourceId} href={`employees/69918`} passHref replace={true}>
<Dropdown.Item>
{element.firstname} - {element.lastname}
</Dropdown.Item>
</Link>
);
})}
</Dropdown>
)}
</form>
The solution was to use onBlur event and hide the dropdown. The problem comes with the onblure is triggered before you press the item. The solution here was to use a setTimeout of 200ms.

In Next.js, how to scroll to top of window after setting search value on a search bar?

Imagine I have a button somewhere. When clicked, the text on it, now goes to my search bar. How do I make the window scroll to the search bar too after the value is set in the Input element shown below?
<Flex
flexDirection="column"
justifyContent="center"
alignItems="center"
maxWidth="90%"
mt={4}
>
{!filteredBlogPosts.length && 'No posts found.'}
{filteredBlogPosts.map((frontMatter) => (
<>
<BlogPost
key={frontMatter.title + frontMatter.lastPublishedOn}
handleSearch={(anyKey) => setSearchValue(anyKey)}
// Insert your code here, how to scroll after setting the key to search?
{...frontMatter}
/>
</>
))}
</Flex>
And, here is the <Input> field
<Input
aria-label="Search"
borderColor="blue.500"
onChange={(e) => setSearchValue(e.target.value)}
placeholder="Search"
value={searchValue}
//or should I do scroll here? How?
autoFocus
onFocus={e => e.currentTarget.select()}
/>
Is this something easy to do? Please present code examples if possible.
Thanks.
If anyone faces this issue in the future, I solved it using this simple function.
const scrollSearch = myKey => {
window.scrollTo(0, 0);
frontMatter.handleSearch(myKey)
};
And passed the scrollSearch function in button onClick.

Influence tab order of Material UI controls

I have an app built up with React and Material UI. Within one view it is possible to have several text fields and several buttons. Now, when I have the focus on one text field and then press Tab I cannot reliably anticipate which one of the controls will be the next one to get the focus. I want to first tab through all the text fields and then secondly tab through all the buttons.
<DialogContent>
<DialogContentText>
The username and password that were used are incorrect. Please provide the correct credentials in order to login to the API.
<Stepper activeStep={this.state.credentialsStep} orientation='vertical'>
{
this.steps.map((label, index) => (
<Step key={label}>
<StepLabel>{label}</StepLabel>
<StepContent>
<Typography>{this.stepContent[index]}</Typography>
{this.stepAction[index]}
<Grid container direction='row' className='m-t-26'>
<Button color='primary'
onClick={() => {
this.state.credentialsStep === 0 ? this.onClickCancel() : this.onClickBack();
}}>
{this.state.credentialsStep === 0 ? 'Cancel' : 'Back'}
</Button>
<Button variant='contained'
color='primary'
onClick={() => {
this.state.credentialsStep === this.steps.length - 1 ? this.onClickLogin() : this.onClickNext();
}}>
{this.state.credentialsStep === this.steps.length - 1 ? 'Login' : 'Next'}
</Button>
</Grid>
</StepContent>
</Step>
))
}
</Stepper>
</DialogContentText>
</DialogContent>
Is there a way to set the tab order of controls?
You can control this with the tabIndex property, but you may be better off to figure out how to have the elements appear in the source in the order you would want the focus to go.
I have found this resource handy: https://bitsofco.de/how-and-when-to-use-the-tabindex-attribute/
When to use a positive tabindex value
There is almost no reason to
ever use a positive value to tabindex, and it is actually considered
an anti-pattern. If you’re finding the need to use this value to
change the order in which elements become focusable, it is likely that
what you actually need to do is change the source order of the HTML
elements.
One of the problems with explicitly controlling tabindex order is that any elements with a positive value are going to come before any other focusable elements that you haven't explicitly put a tabindex on. This means that you could end up with very confusing focus order if you miss any elements that you would want in the mix.
If you want to have the button on the right come before the button on the left in the focus order, there are various CSS options that would allow the button on the right to come first in the source order.
If, however, you decide that explicitly specifying the tabindex is your best option, here is an example showing how to do this for TextField and Button:
import React from "react";
import ReactDOM from "react-dom";
import TextField from "#material-ui/core/TextField";
import Button from "#material-ui/core/Button";
function App() {
return (
<div className="App">
<TextField label="1" inputProps={{ tabIndex: "1" }} />
<br />
<TextField label="3" inputProps={{ tabIndex: "3" }} />
<br />
<TextField label="2" inputProps={{ tabIndex: "2" }} />
<br />
<Button tabIndex="5">Button 5</Button>
<Button tabIndex="4">Button 4</Button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
You may want to use the html attribute tabindex. This allows you to specify the order that tabbing will go through in your form. You can read more about it here and I've put a small example below, setting the tab index of your button to #1
<StepContent>
<Typography>{this.stepContent[index]}</Typography>
{this.stepAction[index]}
<Grid container direction="row" className="m-t-26">
<Button
tabIndex="1" // This will make the button the first tab index for the form.
color="primary"
onClick={() => {
this.state.credentialsStep === 0
? this.onClickCancel()
: this.onClickBack();
}}
>
{this.state.credentialsStep === 0 ? "Cancel" : "Back"}
</Button>
<Button
variant="contained"
color="primary"
onClick={() => {
this.state.credentialsStep === this.steps.length - 1
? this.onClickLogin()
: this.onClickNext();
}}
>
{this.state.credentialsStep === this.steps.length - 1 ? "Login" : "Next"}
</Button>
</Grid>
</StepContent>;
You can use a css trick to render the buttons in reverse order, but with css to reverse the buttons in UI.
<DialogContent>
<DialogContentText>
The username and password that were used are incorrect. Please provide the correct credentials in order to login to the API.
<Stepper activeStep={this.state.credentialsStep} orientation='vertical'>
{
this.steps.map((label, index) => (
<Step key={label}>
<StepLabel>{label}</StepLabel>
<StepContent>
<Typography>{this.stepContent[index]}</Typography>
{this.stepAction[index]}
<Grid container direction='row' className='m-t-26'>
// Box wrapper added <Box style={{ display: 'flex', flexDirection: 'row-reverse', justifyContent: 'flex-end' }}>
// First Button is now "Next in JSX <Button variant='contained'
color='primary'
onClick={() => {
this.state.credentialsStep === this.steps.length - 1 ? this.onClickLogin() : this.onClickNext();
}}>
{this.state.credentialsStep === this.steps.length - 1 ? 'Login' : 'Next'}
</Button>
<Button color='primary'
onClick={() => {
this.state.credentialsStep === 0 ? this.onClickCancel() : this.onClickBack();
}}>
{this.state.credentialsStep === 0 ? 'Cancel' : 'Back'}
</Button>
</Box>
</Grid>
</StepContent>
</Step>
))
}
</Stepper>
</DialogContentText>
</DialogContent>

How can I click the option without input value change in antd autocomplete component

In autocomplete, when you click the option, input value will change,
But I want the input value immutable when I click the option.
I have tried to change the value back in onSelect call back.
Mostly the input value will change to the option value first, then change to the given value, it work, but not that smooth, and sometimes it didn't change.
This is the code
<div className="search-container">
<AutoComplete
className="global-search"
size="large"
dropdownClassName="certain-category-search-dropdown"
dropdownMatchSelectWidth={false}
dropdownStyle={{ width: 300, top: 47 }}
dataSource={options}
onSelect={()=>{this.value = value | ''}}
onSearch={this.inputValueChange}
placeholder={formatMessage(messages.searchPlaceHolder)}
optionLabelProp=""
filterOption={false}
>
<Input
suffix={(
<Button className="search-btn" size="large" type="primary">
<Icon type="search" />
</Button>
)}
/>
</AutoComplete>
</div>
For your question:
But I want the input value immutable when I click the option
I think you can use value + onSelect to make AutoComplete under control, see https://facebook.github.io/react/docs/forms.html#controlled-components

Resources