How to create search in react.js inside a Component - javascript

i have my react redux project, that i am wanting to add a search to the menuItems Component. but i cant seem to figure out how to search over the existing map. ive created a input with the onChange event that sets the state to the input term. but i cant semm what to do after that.
class MenuItems extends Component {
state = {
searchTerm: '',
}
handleclick = (item) => {
this.props.deleteMenuItem(item.id);
}
handleSearch = (event) => {
this.setState({
[event.target.name]: event.target.value
})
}
filteredItem = () => {
let filtered = [...this.props.menuItems].filter(item => item.name === "burger")
}
render(){
let showItems = this.props.menuItems;
return (
<div>
<input
placeholder="search"
name="searchTerm"
type="text"
value={this.state.searchTerm}
onChange={this.handleSearch}
/>
{showItems.map((item) =>(
<li class="list" key={item.id}>
{item.name}
<br></br>
{item.body}
<br></br>
<img src={item.image}></img>
<br></br>
<button id={item.id} onClick={() => this.handleclick(item)}>delete </button>
</li>
))}
</div>
)
}
}
export default connect(null, {deleteMenuItem})(MenuItems)

It looks like you just need to filter with your searchTerm and it will work just fine.
class MenuItems extends Component {
state = {
searchTerm: "",
};
handleclick = (item) => {
this.props.deleteMenuItem(item.id);
};
handleSearch = (event) => {
this.setState({
[event.target.name]: event.target.value,
});
};
render() {
let showItems = this.props.menuItems;
const filtered =
[...this.props.menuItems].filter((item) =>
item.name.includes(this.state.searchTerm)
) ?? [];
return (
<div>
<input
placeholder="search"
name="searchTerm"
type="text"
value={this.state.searchTerm}
onChange={this.handleSearch}
/>
{showItems.map((item) => (
<li className="list" key={item.id}>
{item.name}
<br></br>
<button id={item.id} onClick={() => this.handleclick(item)}>
delete
</button>
</li>
))}
{filtered.map((item) => (
<li className="list" key={item.id}>
Filtered: {item.name}
<br></br>
</li>
))}
</div>
);
}
}
const App = () => {
return (
<div>
<MenuItems menuItems={[{ name: "name1" }, { name: "name2" }]} />
</div>
);
};

Related

on click function not working because its in another return statement

I am trying to make history by pushing on button click
onclick function of the li is not working
as u can see in the code <SuggestionsList/> is in the last return statement its funtions are rendered in const SuggestionsList = (props) => { . The onclick funtion is comimg inside the const SuggestionsList funtion, this is making the onclick funtion not working
i created the exact working in codesand and its working there without any problem i dont get why its not working in my local
enter link description here
function finddoctor(e) {
console.log(e);
history.push(`/detiled/${e} `);
}
const onChange = (event) => {
const value = event.target.value;
setInputValue(value);
setShowResults(false);
const filteredSuggestions = suggestions.filter(
(suggestion) =>
suggestion.firstname
.toString()
.toLowerCase()
.includes(value.toLowerCase()) ||
suggestion.id.toString().toLowerCase().includes(value.toLowerCase())
);
setFilteredSuggestions(filteredSuggestions);
setDisplaySuggestions(true);
};
const onSelectSuggestion = (index) => {
setSelectedSuggestion(index);
setInputValue(filteredSuggestions[index]);
setFilteredSuggestions([]);
setDisplaySuggestions(false);
};
const SuggestionsList = (props) => {
const {
suggestions,
inputValue,
onSelectSuggestion,
displaySuggestions,
selectedSuggestion,
} = props;
if (inputValue && displaySuggestions) {
if (suggestions.length > 0) {
return (
<ul className="suggestions-list" style={styles.ulstyle}>
{suggestions.map((suggestion, index) => {
const isSelected = selectedSuggestion === index;
const classname = `suggestion ${isSelected ? "selected" : ""}`;
return (
<>
<li
style={styles.listyle}
onClick={finddoctor(suggestion.id)}
key={index}
className={classname}
>
{suggestion.firstname}
</li>
</>
);
})}
</ul>
);
} else {
return <div>No suggestions available...</div>;
}
}
return <></>;
};
useEffect(() => {
axios
.get("admin-panel/all-doctors-list/")
.then((res) => {
const data = res.data;
setShowSerch(data);
});
}, []);
return (
<>
<div className="note-container" style={styles.card}>
<div style={styles.inner}>
<p style={{ textAlign: "left" }}>Search Doctors</p>
<form className="search-form" style={{}}>
{showResults ? (
<FontAwesomeIcon
style={{ marginRight: "-23px" }}
icon={faSearch}
/>
) : null}
<input
onChange={onChange}
value={inputValue}
style={styles.input}
type="Search"
/>
<SuggestionsList
inputValue={inputValue}
selectedSuggestion={selectedSuggestion}
onSelectSuggestion={onSelectSuggestion}
displaySuggestions={displaySuggestions}
suggestions={filteredSuggestions}
/>
</form>
</div>
</div>
</>
);
};
Instead of onClick={finddoctor(suggestion.id)} (Here just function invocation is happening and expected to have the callback method)
should be
onClick={() => finddoctor(suggestion.id)}

How to use Formik with dynamicly generated lists (e.g. via AJAX or setState)?

I have a dynamic array of form fields, whose values are fetched via REST API. On the page, there is also a dropdown, that, when changed, shows a different array of fields. I fetch all of these fields/values during the componentDidMount life cycle hook and filter the list to show the relevant data.
The Formik docs mention FieldArrays as a means to handle an array of fields. However, their example shows a static list of objects as its initialValues -- but I don't see how dynamically generated lists. In fact, since I'm fetching initialValues via AJAX, it's initially an empty array -- so nothing is rendered even after getting the data.
This is simplified version of my code:
const MyComponent = class extends Component {
componentDidMount() {
// data structure: [{Name: '', Id: '', Foo: '', Bar: ''}, ...]
axios
.get('/user')
.then((res) => {
this.setState({
userData: res.data
});
});
}
render() {
return (
<div>
<Formik
initialValues={{
users: this.state.userData
}}
render={({values}) => (
<Form>
<FieldArray
name="users"
render={arrayHelpers => (
<ul>
{
values.users.map((user, index) => {
return (
<li key={user.Id}>
<div>{user.Name}</div>
<Field name={`user[${index}].Foo`} type="text" defaultValue={user.Foo} />
<Field name={`user[${index}].Bar`} type="text" defaultValue={user.Bar} />
</li>);
}
}
</ul>
)}
/>
</Form>
)}
/>
</div>
);
}
}
You can do this via setting enableReinitialize true. According to doc it will do this:
Default is false. Control whether Formik should reset the form if initialValues changes (using deep equality).
I created complete codesanbox where your incoming data is async and when you push the data its also async. check this:
import React, { useEffect } from "react";
import ReactDOM from "react-dom";
import { Formik, Field, Form, ErrorMessage, FieldArray } from "formik";
const InviteFriends = () => {
const [initialValues, setInitialValues] = React.useState({
friends: []
});
useEffect(() => {
const initialValues = {
friends: [
{
name: "",
email: ""
}
]
};
const timer = setTimeout(() => {
setInitialValues(initialValues);
}, 1000);
return () => {
timer && clearTimeout(timer);
};
}, []);
return (
<div>
<h1>Invite friends</h1>
<Formik
initialValues={initialValues}
enableReinitialize={true}
onSubmit={async (values) => {
await new Promise((r) => setTimeout(r, 500));
alert(JSON.stringify(values, null, 2));
}}
>
{({ values }) => (
<Form>
<FieldArray name="friends">
{({ insert, remove, push }) => (
<div>
{console.log("Values", values, initialValues)}
{values.friends.length > 0 &&
values.friends.map((friend, index) => (
<div className="row" key={index}>
<div className="col">
<label htmlFor={`friends.${index}.name`}>Name</label>
<Field
name={`friends.${index}.name`}
placeholder="Jane Doe"
type="text"
/>
<ErrorMessage
name={`friends.${index}.name`}
component="div"
className="field-error"
/>
</div>
<div className="col">
<label htmlFor={`friends.${index}.email`}>
Email
</label>
<Field
name={`friends.${index}.email`}
placeholder="jane#acme.com"
type="email"
/>
<ErrorMessage
name={`friends.${index}.name`}
component="div"
className="field-error"
/>
</div>
<div className="col">
<button
type="button"
className="secondary"
onClick={() => remove(index)}
>
X
</button>
</div>
</div>
))}
<button
type="button"
className="secondary"
onClick={async () => {
await new Promise((r) =>
setTimeout(() => {
push({ name: "", email: "" });
r();
}, 500)
);
}}
>
Add Friend
</button>
</div>
)}
</FieldArray>
<button type="submit">Invite</button>
</Form>
)}
</Formik>
</div>
);
};
ReactDOM.render(<InviteFriends />, document.getElementById("root"));
Here is the demo: https://codesandbox.io/s/react-formik-async-l2cc5?file=/index.js

How to access index of .map() method outside of it in react

I want to access the index number outside of that map method because I want to use that index conditionally to show other component like if that index is checked then the component will show but I can't figure out how to access that outside of that map method. I have googled it but couldn't find any proper solution of that.
Here is my code!
import React, { Component, Fragment } from "react";
import Clear from "./clear";
import Display from "./display";
import NoOfItems from "./noOfItems";
class MainPage extends Component {
constructor() {
super();
this.state = {
data: [
{
name: "",
completed: false,
},
],
data: [],
checkValue: false,
};
}
handleChange = (e) => {
e.preventDefault();
this.setState({ name: e.target.value });
};
handleSubmit = (e) => {
e.preventDefault();
this.setState({
data: [...this.state.data, { name: this.state.name, completed: false }],
});
e.target.reset();
};
handleDelete = (index) => {
const newList = [...this.state.data];
newList.splice(index, 1);
this.setState({ data: newList });
};
handleAllCheck = (e) => {
e.preventDefault();
};
handleCheckChange = (index) => {
let newData = [...this.state.data];
newData[index].completed = !newData[index].completed;
this.setState({ data: newData });
};
render() {
return (
<Fragment>
<h1 className="display-1 text-center" style={{ color: "#f7c6c6" }}>
todos
</h1>
<form className="todo-form" onSubmit={this.handleSubmit}>
<label className="label" onClick={this.handleAllCheck}>
^
</label>
<input
autoFocus
type="text"
onChange={this.handleChange}
className="new-todo shadow-lg p-3 mb-5 bg-white"
placeholder="What needs to be done?"
/>
<ul className="list-group">
{this.state.data.map((data, index) => {
return (
<div key={"todo-" + index} className="div-list">
<input
className="check"
onChange={() => this.handleCheckChange(index)}
type="checkbox"
style={{
cursor: "pointer",
}}
defaultChecked={this.state.data.completed}
/>
<li
className="list-group-item disabled w-50 p-3 mx-auto"
style={{
textDecoration:
this.state.data[index].completed && "line-through",
}}
>
{data.name}
</li>
<button
onClick={() => this.handleDelete(index)}
type="button"
className="close"
aria-label="Close"
>
<span aria-hidden="true">×</span>
</button>
</div>
);
})}
</ul>
{this.state.data.length > 0 && <Display />}
{this.state.data.length > 0 && (
<NoOfItems noOfTodos={this.state.data.length} />
)}
{this.state.data.completed && <Clear />}
</form>
</Fragment>
);
}
}
export default MainPage;
Just ignore the rest and see the map method and its index. At the end of the code I have used this.state.data.completed at this point I want to use the index like this this.state.data[index].completed but its saying invalid declaration of Index. Please help me I am stuck!
Thank you in advance!

Multiple fields appearing under a parent in ReactJs

class CriteriaSetValue extends Component {
state = {
count: 0,
tableName: "",
fieldName:"",
tables: [],
fields: [],
addLine: [],
addField: []
};
onSubmit = e => {
e.preventDefault();
const addField = this.state.addField;
const size = addField.length + 1;
addField.push(size);
this.setState({
addField
});
addNewLine = event => {
event.preventDefault();
const addLine = this.state.addLine;
const size = addLine.length + 1;
const nextLine = this.state.count + 1;
addLine.push(size);
this.setState({
count: nextLine,
addLine
});
};
render() {
const {showForm, tableName, fieldName } = this.state;
const { props, state } = this;
return (
<React.Fragment>
<div className="form-wrapper">
<div className="row">
<div className="col-10 container">
<form onSubmit={this.submit} className="card">
<div className="card-header">
<h3 className="card-title">
Criteria Set
{/* <Locale value="std.formupload" /> */}
</h3>
</div>
<div className="card-body">
<div className="row">
<div className="col-md-7 col-lg-8">
<div className="add-line">
<Button
icon
labelPosition="left"
onClick={this.addNewLine}
>
Add Line
<Icon name="plus" />
</Button>
{this.state.addLine.map(index => {
return (
<div
className="field-line"
style={{ marginTop: "30px" }}
key={index}
id={index}
>
<h4 className="field-button">Line {index}</h4>
<Button
className="field-button"
icon
onClick={this.toggleForm}
>
<Icon name="box" />
</Button>
</div>
);
})
}
{
this.state.addField.map(index => {
return (
<React.Fragment>
<div
className="field-button"
style={{
marginTop: "20px",
paddingLeft: "20px"
}}
key={index}
id={index}
>
<h4
className="field-button"
onclick={this.addCriteriaValue}
>
<span>
table.field
</span>
</h4>
<Button
className="field-button"
icon
onClick={this.toggleDelete}
>
<Icon name="delete calendar" />
</Button>
</div>
<br></br>
</React.Fragment>
);
})
}
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</React.Fragment>
);
}
};
This is kind of related to what I am trying to achieve:
https://codesandbox.io/s/3vqyo8xlx5
But I want the child to appear under the preference of where the "Add child" button is clicked, not on the last one.
I am working on making multiple fields to appear under each line when a button beside line1, line2 and so on is clicked, but currently, the field jump to last lines when a button is clicked, it is not appearing on the appropriate line.
I have been able to show lines when the "Add Line" button is clicked and I have also been able to show the field when the button beside "Line #1" is clicked.
I want the fields for "Line #1" to show under the line1 when the field button is clicked, the field for "Line #2" to show under the line2 when the field button beside it is clicked and so on
You can try something like this:
const AddButton = ({ label, onClick }) => (
<button onClick={onClick}>
{label} [+]
</button>
)
const FieldData = ({ label, criterias, onAdd, onDelete }) => (
<React.Fragment>
<div
className='field-button'
style={{
marginTop: '20px',
paddingLeft: '20px'
}}
>
<h4 className='field-button'>
<AddButton
label='Add criteria'
onClick={onAdd}
/>
<span>
{label}
</span>
</h4>
{criterias.map((item, idx) => (
<p key={idx}>{item.id}</p>
))}
<button
className='field-button'
onClick={onDelete}
>
Del [-]
</button>
</div>
<br />
</React.Fragment>
)
class App extends React.PureComponent {
state = {
lines: []
}
handleSubmit = e => {
e.preventDefault()
console.log('DATA TO SAVE ' + JSON.stringify(this.state.lines))
}
handleAddLine = event => {
event.preventDefault()
this.setState(prevState => ({
...prevState,
lines: [
...prevState.lines,
{
id: (prevState.lines.length + 1),
fields: []
}
]
}))
}
handleAddField = lineId => e => {
e.preventDefault()
this.setState(prevState => {
const newLines = [ ...prevState.lines ]
const curLine = newLines[newLines.findIndex(({ id }) => id === lineId)]
curLine.fields.push({
id: curLine.fields.length + 1,
criterias: []
})
return {
...prevState,
lines: newLines
}
})
}
handleAddCriteria = (lineId, fieldId) => event => {
event.preventDefault()
this.setState(prevState => {
const newLines = [ ...prevState.lines ]
const curLine = newLines[newLines.findIndex(({ id }) => id === lineId)]
const curField = curLine.fields[curLine.fields.findIndex(({ id }) => id === fieldId)]
curField.criterias.push({
id: curField.criterias.length + 1
})
return {
...prevState,
lines: newLines
}
})
}
handleDeleteField = (lineId, fieldId) => event => {
event.preventDefault()
this.setState(prevState => {
const newLines = [ ...prevState.lines ]
const curLine = newLines[newLines.findIndex(({ id }) => id === lineId)]
curLine.fields = curLine.fields.filter(item => item.id !== fieldId)
return {
...prevState,
lines: newLines
}
})
}
render() {
const { lines } = this.state
return (
<React.Fragment>
<div className='form-wrapper'>
<div className='row'>
<div className='col-10 container'>
<form
onSubmit={this.handleSubmit}
className='card'
>
<div className='card-header'>
<h3 className='card-title'>
Criteria Set
</h3>
</div>
<div className='card-body'>
<div className='row'>
<div className='col-md-7 col-lg-8'>
<div className='add-line'>
<AddButton
label='Add Line'
onClick={this.handleAddLine}
/>
{lines.map((line, idx) => {
return (
<React.Fragment key={idx}>
<div
className='field-line'
style={{ marginTop: '30px' }}
>
<h4 className='field-button'>
Line {idx+1}
</h4>
<AddButton
label='Add field'
onClick={this.handleAddField(line.id)}
/>
</div>
{line.fields.map((lField, idx) => (
<FieldData
label={idx+1}
criterias={lField.criterias}
onAdd={this.handleAddCriteria(line.id, lField.id)}
onDelete={this.handleDeleteField(line.id, lField.id)}
/>
))}
</React.Fragment>
)
})}
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</React.Fragment>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('app')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
I defined a new data structure for the state, so It is better to think about nested data, and I refactored the code a little.

Add prop when onClick in ReactJS

I would like to change current li item color when I click it.
How to add prop to item(using array map), when I click it? I use styled-components
const Li = styled.li`
color: ${props => (props.checked ? "red" : "green")};
`;
class App extends Component {
constructor(props) {
super(props);
this.state = {
value: "",
items: []
};
}
render() {
const ShowItems = this.state.items.map((item, index) => {
return (
<Li key={index}>
{item}
<button onClick={() => this.deleteItemHandler(index)}> Delete</button>
</Li>
);
});
return (
<Wrapper>
<AddItem
addItemHandler={this.addItem}
InputValue={this.state.value}
InputValueHandler={this.inputValue}
/>
{ShowItems}
</Wrapper>
);
}
}
Check out this code working on CodeSandBox
import React, { Component } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
import styled from "styled-components";
const Li = styled.li`
color: ${props => (props.checked ? "red" : "green")};
`;
class App extends Component {
state = {
value: "",
items: [],
selected: -1
};
handleChange = e => {
this.setState({
[e.currentTarget.name]: e.currentTarget.value
});
};
handleAdd = () => {
const { items, value } = this.state;
this.setState({
items: [...items, value],
value: ""
});
};
handleRemove = index => {
const { items, selected } = this.state;
items.splice(index, 1);
if (index < selected) index = selected - 1;
if (index === selected) index = -1;
if (index > selected) index = selected;
this.setState({
items: items,
selected: index
});
};
handleActiveItem = index => {
this.setState({ selected: index });
};
render() {
const { value, items, selected } = this.state;
return (
<div>
<input
type="text"
value={value}
name="value"
onChange={this.handleChange}
/>
<button
style={{ margin: "0px 5px" }}
disabled={!value}
className="btn btn-sm btn-success"
onClick={this.handleAdd}
>
+
</button>
<ul className="li">
{items.map((item, index) => (
<Li key={index} checked={selected === index}>
<span onClick={() => this.handleActiveItem(index)}>{item}</span>
<button
style={{ margin: "1px 5px" }}
className="btn btn-sm btn-danger"
onClick={() => this.handleRemove(index)}
>
X
</button>
</Li>
))}
</ul>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Ignore the handlers if you don't need them. Thanks to this effort I learnt about styled-components and discovered CodeSandBox.
EDIT :
Added a <span> inside <li> to avoid nested onClick, previously <li> (parent) and <button> (child) both had onClick attribute. On button Click two onClick events were fired resulting in unexpected behaviour in some use cases. You must correct this in your code.
Added logic to keep item selected when an item before it is deleted.
Added button validation to avoid adding empty string/items in list.
Also updated CodeSandBox Code to reflect above changes.
So you need keep track of the active index, and use it too change the color of the active component color.
state ={
activeIndex: void 0
}
const Li = styled.li`
color: ${props => props.checked ? "red" : "green"};
;`
deleteItemHandler = (index) => {
this.setState({
activeIndex: index
})
}
render() {
const ShowItems = this.state.items.map((item, index) => {
return (
<Li key={index} checked={index === this.state.activeIndex} > {item} < button onClick={() => this.deleteItemHandler(index)
}> Delete</button ></Li >
)
})
return (
<Wrapper>
<AddItem
addItemHandler={this.addItem}
InputValue={this.state.value}
InputValueHandler={this.inputValue}
/>
{ShowItems}
</Wrapper>
);
Try this
const Li = styled.li`
color: ${props => props.checked ? "red" : "green"};
;`
class App extends Component {
constructor(props) {
super(props);
this.state = {
value: "",
items: [],
currentChecked: null
};
}
render() {
const ShowItems = this.state.items.map((item, index) => {
return (
<Li key={index} checked={index === this.state.currentChecked} >
{item}
<button onClick={() => this.setState({currentChecked: index})}>Delete</button >
</Li >
)
})
return (
<Wrapper>
<AddItem
addItemHandler={this.addItem}
InputValue={this.state.value}
InputValueHandler={this.inputValue}
/>
{ShowItems}
</Wrapper>
);

Categories

Resources