unable to perform dynamic search in react app using static data - javascript

I am performing sort , filter and pagination on my react app which displays data from a json file which includes movies data. I am able to fetch data from input box but unable to setState and also console log searchKey and movies.
Below is the code for App.js and Hello.js. App component has both sort and search functions.
App.js
import data from './data.json';
class App extends Component {
constructor() {
super();
this.state = {
movies: data.movies, sort_term: '', searchKey: '', filterList: '',
};
this.onSorting = this.onSorting.bind(this);
this.onSearchMovie = this.onSearchMovie.bind(this);
}
onSorting = (e) => {
let term = 'Title';
let option = e.target.value;
let sortedList = [...this.state.movies].sort((a, b) => {
return (option == 'asc' ? (a[term] <= b[term] ? -1 : 1) :
(a[term] >= b[term] ? -1 : 1))
});
this.setState({ sort_term: term });
this.setState({ movies: sortedList });
}
onSearchMovie(e) {
let key = e;
console.log(key);
this.setState({searchKey : key });
console.log(searchKey);
let list = movies.filter(m =>{
return m.Title.toLowerCase().includes(searchKey.toLowerCase());
});
this.setState({movies:list});
}
render() {
const { movies } = this.state;
if (!movies.length) {
return (
<div className="container">
<h2>loading...</h2>
</div>
);
}
else {
return (
<div>
<Hello sort_term={this.state.sort_term}
onSorting={this.onSorting}
onSearch={this.onSearchMovie} />
<br />
<Table movies={this.state.movies} />
</div>
);
}
}
}
Hello.js
class Hello extends Component {
constructor(props) {
super(props);
this.state = { inputField: '', }
this.onSubmitTitle = this.onSubmitTitle.bind(this);
this.getTitle = this.getTitle.bind(this);
}
onSubmitTitle(e){
e.preventDefault();
this.props.onSearch(this.state.inputField);
this.setState({ inputField: '' });
}
getTitle(e){
this.setState({ inputField: e.target.value });
// console.log('---');
// console.log(this.state.inputField);
}
render() {
const { sort_term, onSorting } = this.props;
return (
<div className="header">
Database
<ul className="navLeft">
<li>
<form onSubmit={this.onSubmitTitle}>
<input type="search" onChange={this.getTitle} value={this.state.inputField}
className="search-bar"
placeholder="Type for search..." />
</form>
</li>
<li >
<form >
<select
onChange={onSorting}
className="searchBar">
<option value="desc" >Sort Title(Z - A)</option>
<option value="asc">Sort Title(A - Z)</option>
<option value="descDirector">Sort Director(Z - A)</option>
<option value="ascDirector">Sort Director(A - Z)</option>
</select>
</form>
</li>
</ul>
</div>
);
}
}

You got couple of issues:
You got some undefined variables here:
onSearchMovie(e) {
let key = e;
console.log(key);
this.setState({ searchKey: key });
console.log(searchKey);
let list = movies.filter(m => {
return m.Title.toLowerCase().includes(searchKey.toLowerCase());
});
this.setState({ movies: list });
}
like `searchKey` and `movies`.
setState is asynchronous, so doing this:
this.setState({ sort_term: term });
this.setState({ movies: sortedList });
Is the same as doing this:
this.setState({
sort_term: term,
movies: sortedList
});
With that saying, this code block may not work as expected:
onSearchMovie(e) {
let key = e;
console.log(key);
this.setState({ searchKey: key });
console.log(searchKey);
let list = movies.filter(m => {
return m.Title.toLowerCase().includes(searchKey.toLowerCase());
});
this.setState({ movies: list });
}
Because as mentioned above, setState is asynchronous. so this.state.searchKey may not be what you think when you access it the next line.
Either use the key variable or use the setState callback which will trigger after setState has done.
this.setState({someKey: someValue}, () => {console.log(this.state.someKey)})
You can read more about it in the docs Why is setState giving me the
wrong value?.
Another thing you may not know is that when you use onSubmit on a
form you actually need to trigger the submit. Since you don't have
any submit button you can do it with the enter key. so while you
are focused on the input press the enter key and the form will
get submitted.
Another thing i would do, is to display and update a filteredMovis
list instead of the movies list directly because once you filter
out items then you can't get them back. so when you do filtering you
do it on movies but updating the filteredMovis list without
removing items from the original movies list.
Here is a working and running example:
const data = {
movies: [
{
id: 1,
Title: "wonder woman"
},
{
id: 2,
Title: "kill bill"
},
{
id: 3,
Title: "world war z"
},
{
id: 4,
Title: "pixels"
}
]
};
class Hello extends React.Component {
constructor(props) {
super(props);
this.state = { inputField: "" };
this.onSubmitTitle = this.onSubmitTitle.bind(this);
this.getTitle = this.getTitle.bind(this);
}
onSubmitTitle(e) {
e.preventDefault();
this.props.onSearch(this.state.inputField);
this.setState({ inputField: "" });
}
getTitle(e) {
this.setState({ inputField: e.target.value });
// console.log('---');
// console.log(this.state.inputField);
}
render() {
const { onSorting } = this.props;
return (
<div className="header">
Database
<ul className="navLeft">
<li>
<form onSubmit={this.onSubmitTitle}>
<input
type="text"
onChange={this.getTitle}
value={this.state.inputField}
className="search-bar"
placeholder="Type for search..."
/>
</form>
</li>
<li>
<form>
<select onChange={onSorting} className="searchBar">
<option value="desc">Sort Title(Z - A)</option>
<option value="asc">Sort Title(A - Z)</option>
<option value="descDirector">Sort Director(Z - A)</option>
<option value="ascDirector">Sort Director(A - Z)</option>
</select>
</form>
</li>
</ul>
</div>
);
}
}
class App extends React.Component {
constructor() {
super();
this.state = {
movies: data.movies,
filteredMovis: data.movies,
sort_term: "",
searchKey: "",
filterList: ""
};
this.onSorting = this.onSorting.bind(this);
this.onSearchMovie = this.onSearchMovie.bind(this);
}
onSorting = e => {
let term = "Title";
let option = e.target.value;
let sortedList = [...this.state.movies].sort((a, b) => {
return option == "asc"
? a[term] <= b[term]
? -1
: 1
: a[term] >= b[term]
? -1
: 1;
});
this.setState({
sort_term: term,
filteredMovis: sortedList
});
};
onSearchMovie(key) {
const { movies } = this.state;
let list = movies.filter(m => {
return m.Title.toLowerCase().includes(key.toLowerCase());
});
this.setState({ filteredMovis: list, searchKey: key });
}
render() {
const { filteredMovis, movies } = this.state;
if (!movies.length) {
return (
<div className="container">
<h2>loading...</h2>
</div>
);
} else {
return (
<div>
<Hello
sort_term={this.state.sort_term}
onSorting={this.onSorting}
onSearch={this.onSearchMovie}
/>
<br />
{filteredMovis.map(movie => <div key={movie.id}>{movie.Title}</div>)}
</div>
);
}
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Related

How to change the checkbox selection and get the updated id's of checkbox items in form submit in React Component?

I need help in my react code. I am displaying the list of checkbox items using api. My JSON file contains the data which is having IsAssigned True/False. If isAssigned is true for that item, item checkbox will be pre-checked on page load.
Now, I want to change the selection of Checkboxlist items. How can I get the id's of selected Checkboxes and call post/put api calls to make changes in the database.
There are checkboxes that are pre-selected isAssigned=true, unselect the pre-selected checkboxes isAssigned= false, select the checkboxes that are not pre-selected earlier.
Please find my react code and json data below. Also, help me out on achieving the above,
import React from "react";
export class SetDescItems extends React.Component {
static displayName = Assign_TemplateDescriptions.name;
constructor(props) {
super(props);
this.state = {
ProdDescrlist: [],
checkedItems: new Map(),
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
var isChecked = event.target.checked;
var item = event.target.value;
var id = event.target.id;
this.setState(prevState => ({ checkedItems: prevState.checkedItems.set(id, item, isChecked) }));
}
handleSubmit(event) {
event.preventDefault();
//Create an Array.
var checkedItems = new Array();
//Reference all the CheckBoxes
var chks = document.getElementsByTagName("INPUT");
// Loop and push the checked CheckBox id's in Array.
for (var i = 0; i < chks.length; i++) {
if (chks[i].checked) {
checkedItems.push(chks[i].id);
}
}
//Display the selected CheckBox id's.
if (checkedItems.length > 0) {
checkedItems = checkedItems.join(",");
console.log("Selected ids: " + checkedItems);
}
const data = checkedItems;
//write post api here
}
componentDidMount() {
this.getProdDescriptions();
}
async getProdDescriptions () {
const url = “/api/getDescriptions”;
await fetch(url)
.then(response => response.json())
.then((data) => {
this.setState({
ProdDescrlist: data,
loading: false
})
console.log(this.state.ProdDescrlist)
})
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit} >
<ul style={{ listStyle: 'none' }}>
{
(this.state.ProdDescrlist.map((item, index) => {
return (
<li key={index}>
<input type="checkbox"
id={item.ProdDescription_ID}
defaultChecked={item.isAssigned == "TRUE"?true:false}
value={item.DescriptionTextPlain}
onChange={this.handleChange} />
{item.DescriptionTextPlain}
</li>
)
}
))
}
</ul>
<button type="submit">Submit</button>
</form>
</div>
);
}
}
export default SetDescItems;
[{"ProdDescription_ID":2863,"isAssigned":"TRUE","DisplaySequence":0,"DescriptionTextPlain":"A1Testig"},
{"ProdDescription_ID":2865,"isAssigned":"TRUE","DisplaySequence":0,"DescriptionTextPlain":"test"},
{"ProdDescription_ID":1778,"isAssigned":"FALSE","DisplaySequence":0,"DescriptionTextPlain":"Testing4"},
{"ProdDescription_ID":2864,"isAssigned":"FALSE","DisplaySequence":0,"DescriptionTextPlain":"A2 "},
{"ProdDescription_ID":1544,"isAssigned":"FALSE","DisplaySequence":5,"DescriptionTextPlain":"ProductGuide"},
{"ProdDescription_ID":1535,"isAssigned":"TRUE","DisplaySequence":10,"DescriptionTextPlain":"testexample"},
{"ProdDescription_ID":1536,"isAssigned":"FALSE","DisplaySequence":15,"DescriptionTextPlain":"Note"}]
You need to use checked prop in input tag
import "./styles.css";
import React from "react";
export class SetDescItems extends React.Component {
constructor(props) {
super(props);
this.state = {
ProdDescrlist: [],
checkedItems: []
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
const id = parseInt(event.target.id, 10);
const index = this.state.checkedItems.indexOf(id);
const updatedArray = [...this.state.checkedItems];
if (index !== -1) {
updatedArray.splice(index, 1);
} else {
updatedArray.push(id);
}
this.setState((prevState) => ({
checkedItems: updatedArray
}));
}
handleSubmit(event) {
event.preventDefault();
let value = "";
this.state.checkedItems.forEach((item) => {
if (item) {
value = value === "" ? item : value + "," + item;
}
});
if (value !== "") {
alert(value);
}
}
componentDidMount() {
this.getProdDescriptions();
}
async getProdDescriptions() {
const url = "/api/getDescriptions";
await fetch(url)
.then((response) => response.json())
.then((data) => {
let items = data.reduce((acc, item) => {
if(item.isAssigned === "TRUE") acc.push(item.ProdDescription_ID);
return acc;
}, []);
this.setState({
ProdDescrlist: data,
loading: false,
checkedItems: items
});
});
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<ul style={{ listStyle: "none" }}>
{this.state.ProdDescrlist.map((item, index) => {
return (
<li key={index}>
<input
type="checkbox"
id={item.ProdDescription_ID}
defaultChecked={item.isAssigned === "TRUE" ? true : false}
value={item.DescriptionTextPlain}
onChange={this.handleChange}
/>
{item.DescriptionTextPlain}
</li>
);
})}
</ul>
<button type="submit">Submit</button>
</form>
</div>
);
}
}

React class component. Todo app. How to store data with localStorage

I'd like to store todo data with localStorage so that it won't disappear after refreshing the page.
I used React class component when started creating.
I've added 'handleFormSubmit' and 'ComponentDidMount' methods.
nothing stores in localStorage when I type todo and choose date.
get an error in ComponentDidMount with
Line 'const result = localData ? JSON.parse(localData) : [];'
:SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data
how can I set and get items?
It would be really appreciated if I could get help.
I'd like to make this app really work.
import React from "react"
import TodoItem from "./components/TodoItem"
import todosData from "./components/todosData"
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
todos: todosData,
//setTodos: todosData,
newItem: "",
deadline: "",
editing: false
}
this.handleChange = this.handleChange.bind(this)
this.addTodo = this.addTodo.bind(this)
this.updateInput = this.updateInput.bind(this)
this.deleteItem = this.deleteItem.bind(this)
this.updateItem = this.updateItem.bind(this)
this.updateDeadline = this.updateDeadline.bind(this)
this.updateInputDeadline = this.updateInputDeadline.bind(this)
this.editItem = this.editItem.bind(this)
this.handleFormSubmit = this.handleFormSubmit.bind(this)
}
handleChange(id) {
this.setState((prevState) => {
const updatedTodos = prevState.todos.map((todo) => {
if (todo.id === id) {
return { ...todo, completed: !todo.completed };
} else {
return todo;
}
});
return { todos: updatedTodos };
});
}
addTodo(e) {
e.preventDefault();
const newTodo = {
id: this.state.todos.length + 1,
text: this.state.newItem,
completed: false,
deadline: this.state.deadline
}
const newTodos = this.state.todos.concat([newTodo]);
this.setState({
todos: newTodos
})
}
updateInput(value, id) {
this.setState((prevState) => {
const updatedTodos = prevState.todos.map((todo) => {
if(todo.id === id) {
return {...todo, text: value}
}else {
return todo;
}
})
return {todos: updatedTodos}
})
}
updateInputDeadline(value, id) {
this.setState((prevState) => {
const updatedTodos = prevState.todos.map((todo) => {
if(todo.id === id) {
console.log(value, id);
return {...todo, deadline: value}
}else {
return todo;
}
})
return {todos: updatedTodos}
})
}
updateItem(e) {
this.setState({
newItem: e.target.value
})
}
updateDeadline(e) {
this.setState({
deadline: e.target.value
})
}
deleteItem(id){
const filteredItems= this.state.todos.filter(item =>
item.id!==id);
this.setState({
todos: filteredItems
})
}
editItem(id) {
this.setState({
editing: id
})
}
handleFormSubmit() {
const { todo, deadline } = this.state;
localStorage.setItem('todo', JSON.stringify(todo));
localStorage.setItem('deadline', deadline);
};
componentDidMount() {
const localData = localStorage.getItem('todo');
const result = localData ? JSON.parse(localData) : [];
const deadlineData = localStorage.getItem('deadline');
this.setState({ result, deadlineData });
}
render() {
const todoItems = this.state.todos.map
(item =>
<TodoItem
key={item.id}
item={item}
handleChange={this.handleChange}
addTodo={this.addTodo}
deleteItem={this.deleteItem}
updateInput={this.updateInput}
updateInputDeadline={this.updateInputDeadline}
isEdited={this.state.editing === item.id}
editItem={this.editItem}
/>)
return (
<div className="todo-list">
<Timer />
<form onSubmit={this.handleFormSubmit}>
<div className="add-todo">
<label>Add an item...</label>
<input
type="text"
name="todo"
placeholder="Type item here..."
value={this.state.newItem}
onChange={this.updateItem}
/>
</div>
<div className="date">
<label htmlFor="deadline">Deadline</label>
<input
type="date" id="start" name="deadline"
min="2021-01-01"
max="2024-12-31"
value={this.state.deadline}
onChange={this.updateDeadline}
/>
</div>
<button type="submit" onClick={this.addTodo}>Add to the list</button>
</form>
{todoItems.length === 0 ? <p>No items</p> : null}
<div className="todoitems">
{todoItems}
</div>
</div>
)
}
}
export default App
When you press the button, there are two events that you are trying to call - addTodo and handleFormSubmit. Since you are calling e.preventDefault() in addTodo, the submit event is never called. You could do all of the actions you need in one of the methods.
My guess is that you are either trying to JSON.parse an array instead of an object, or the value of todo is undefined. You are trying to get todo out of this.state, but you only have todos in your state, so it might be a typo. The same goes for deadline.
You are doing the setting and getting correctly. You could actually get data from localStorage even when you are first setting the state in constructor. But the componendDidMount approach you tried is also good.
constructor(props) {
super(props)
const cachedTodos = localStorage.getItem("todo")
this.state = {
todos: cachedTodos ?? todosData,
...
}

how to display data on click react

Implemented writing data from the form to the array. Everything works. but now I want to implement data output when I click on " SEND ARR "
When I hang up a click on a button, respectively, the data is not displayed since in the function we access event.target.value
Please tell me how to rewrite the line so that I can display data when I click on the button? thank
home.js
import React from "react";
import "./../App.css"
export default class Home extends React.Component {
constructor() {
super()
this.state = {
count: 1,
objexm: '',
inputValue: '',
arr: []
}
this.handleClick = this.handleClick.bind(this);
this.updateInputValue = this.updateInputValue.bind(this);
}
handleClick() {
this.setState({
count: this.state.count + 1
});
};
createMarkup() {
return {
__html: this.state.inputValue
};
};
updateInputValue(event) {
let newArr = this.state.arr;
let newlist = event.target.value;
if (event.target.value) {
newArr.push(newlist)
}
this.setState({
inputValue: newArr
});
event.target.value = '';
};
render() {
return (
<div className="home-header">
<h2>{this.state.count}</h2>
<button onClick={this.handleClick}>Add</button>
<input type='text' name="names" onClick={this.updateInputValue} />
{this.state.arr.map((arrs, index) => {
return (
<li
key={index}
>{arrs}</li>
)
})}
<button>SEND ARR</button>
<ul className="qwe">
<li dangerouslySetInnerHTML={this.createMarkup()}></li>
</ul>
</div>
);
}
}
Store the data from the input into invputValue each time the input is updated and on the click of the button update the arr content with the old values (...arr) plus the current input value (this.state.inputValue) .
To make sure the old values are not deleted the arr is defined at the top of the class let arr = []. If you don't want it there you can instantiate it in the constructer which will run only once. i.e. this.arr = []
let arr = []
class Home extends React.Component {
constructor() {
super()
this.state = {
count: 1,
objexm: '',
inputValue: '',
arr: []
}
this.handleClick = this.handleClick.bind(this);
this.updateInputValue = this.updateInputValue.bind(this);
}
handleClick() {
this.setState({
count: this.state.count + 1
});
};
createMarkup() {
return {
__html: this.state.inputValue
};
};
updateInputValue = e => {
this.setState({ inputValue: e.target.value })
}
displayData = () => {
arr = [...arr ,this.state.inputValue]
this.setState({ arr, inputValue: "" })
}
clearData = () => {
this.setState({ arr: [] })
}
render() {
console.log("this.state.arr:", this.state.arr)
return (
<div className="home-header">
<h2>{this.state.count}</h2>
<button onClick={this.handleClick}>Add</button>
<input type='text' name="names" onChange={this.updateInputValue} value={this.state.inputValue} />
{this.state.arr.map((arrs, index) => {
return (
<li
key={index}
>{arrs}</li>
)
})}
<button onClick={this.displayData}>SEND ARR</button>
<button onClick={this.clearData}>CLEAR ARR</button>
<ul className="qwe">
<li dangerouslySetInnerHTML={this.createMarkup()}></li>
</ul>
</div>
);
}
}
Instead of using onClick on input, use onChange and update value in state i.e. make the input a controlled component . Post that onClick of button take the value from state and push to the array and clear the input value
export default class Home extends React.Component {
constructor() {
super()
this.state = {
count: 1,
objexm: '',
inputValue: '',
arr: []
}
this.handleClick = this.handleClick.bind(this);
this.updateInputValue = this.updateInputValue.bind(this);
}
handleClick() {
this.setState(prevState => ({
count: prevState.count + 1,
inputValue: [...prevState.inputValue, prevState.name],
name: ""
}));
};
createMarkup() {
return {
__html: this.state.inputValue
};
};
updateInputValue(event) {
let newVal = event.target.value;
this.setState({
name: newVal
});
};
render() {
return (
<div className="home-header">
<h2>{this.state.count}</h2>
<button onClick={this.handleClick}>Add</button>
<input type='text' name="names" value={this.state.name} onChange={this.updateInputValue} />
{this.state.arr.map((arrs, index) => {
return (
<li
key={index}
>{arrs}</li>
)
})}
<button>SEND ARR</button>
<ul className="qwe">
<li dangerouslySetInnerHTML={this.createMarkup()}></li>
</ul>
</div>
);
}
}

Adding a list item via a button in React?

Here's the parts of my code that are useful:
class Answers extends Component {
constructor(props) {
super(props);
this.state = {
answers: Array(4).fill(""),
correctAnswers: [],
};
this.handleUpdate = this.handleUpdate.bind(this);
}
// let event = {
// index: 1,
// value: 'hello'
// };
handleUpdate (event) {
//if ("1" == 1) // true
//if ("1" === 1) //false
var answers = this.state.answers;
answers[event.index] = event.value;
this.setState(() => ({
answers: answers
}));
console.log(event);
}
render () {
return (
<div id="answers">
Answer Choices<br />
{this.state.answers.map((value, index) => (
<Answer value={value} key={index} onUpdate={this.handleUpdate} number={index}/>
))}
</div>
);
}
}
class Answer extends Component {
constructor(props) {
super(props);
this.state = {
inputText: "",
answer: props.value,
correctAnswers: "",
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
const target = event.target;
const value = target.type === "checkbox" ? target.checked : target.value;
const name = target.name;
this.setState((previousState, props) => ({
answer: value
}));
this.props.onUpdate({
index: this.props.number,
value
});
//
// let sample = {
// kyle: "toast",
// cam: "pine"
// };
// sample.kyle
// sample.cam
}
render () {
return (
<div>
<input type="checkbox"/>
<input type="text" value={this.state.answer} onChange={this.handleChange}/>
</div>
)
}
}
var questionIdx = 0;
class Questions extends Component {
constructor(props){
super(props);
this.state = {
questions:[]
}
this.handleUpdate = this.handleUpdate.bind(this);
}
handleUpdate (event) {
//if ("1" == 1) // true
//if ("1" === 1) //false
var questions = this.state.questions
questions[event.index] = event.value;
this.setState(() => ({
questions: questions
}));
console.log(event);
}
render () {
return (
<div id="questions">
<ol id="quesitonsList">
<li id="oneQuestion">
<Question onUpdate={this.handleUpdate} number={questionIdx}/>
</li>
</ol>
</div>
);
}
}
class Question extends Component {
constructor(props){
super(props);
this.state = {
question: ""
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
const target = event.target;
const value = target.type === "checkbox" ? target.checked : target.value;
const name = target.name;
this.setState((previousState, props) => ({
question: value
}));
//if (this.prop.questions.indexOf(value) == questionIdx) {
this.props.onUpdate({
index: questionIdx,
value
});
// }
}
render () {
return (
<div id="question">
Question<br />
<input type="text" value={this.state.question} onChange={this.handleChange}/>
<PhotoDropZone />
<Answers />
</div>
);
}
}
class AddQuestionButton extends Component {
addQuestion () {
}
render () {
return (
<div id="addQuestionButton">
<button id="addQuestionButton" onClick={this.addQuestion}>Add Question</button>
</div>
);
}
}
So what I am having trouble figuring out is how to go about using my addQuestionButton to add
<li id="oneQuestion">
<Question onUpdate={this.handleUpdate} number={questionIdx}/>
</li>
to my questionsList <ol></ol> in my Questions class. I'm having a hard time figuring out how to approach this, I'm new to React and JS. I don't need the exact answer per say but a hint in the right direction would help. Thanks!
You could create a function in your Questions that adds a question to your questions state array and pass that down as a prop to your AddQuestionButton component.
Example
class Questions extends React.Component {
state = {
questions: ["What is this?"]
};
addQuestion = question => {
this.setState(prevState => ({
questions: [...prevState.questions, question]
}));
};
render() {
return (
<div id="questions">
<ol id="quesitonsList">
{this.state.questions.map(question => (
<li id="oneQuestion"> {question} </li>
))}
</ol>
<AddQuestionButton onClick={this.addQuestion} />
</div>
);
}
}
class AddQuestionButton extends React.Component {
addQuestion = () => {
this.props.onClick(
Math.random()
.toString(36)
.substring(7) + "?"
);
};
render() {
return (
<div id="addQuestionButton">
<button id="addQuestionButton" onClick={this.addQuestion}>
Add Question
</button>
</div>
);
}
}
ReactDOM.render(<Questions />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Perhaps it may help you:
const questionsList = [1, 2, 3, 4, 5];
const listItems = questionsList.map((item, index) =>(
<li key="{index}">
<Question onUpdate={this.handleUpdate} number={questionIdx}/>
</li>
));
ReactDOM.render(
<ol>{listItems}</ol>
);

Arrays - Javascript - filter array of objects using input search

I am trying to filter a list using react, but surprisingly, for such a common task, cannot find anything online to help me achieve this.
I have an array of users which I then want to filter through (starting off with name - I can work out filtering by age then).
The array is in my redux store and looks like the below.
let users = [
{
name: 'Raul',
age: 29
},
{
name: 'Mario',
age: 22
}
];
My entire component looks like the below.
class Test extends Component {
constructor(props) {
super(props);
this.state = {
users: this.props.users
};
this.filterList = this.filterList.bind(this);
}
componentWillReceiveProps(nextProps) {
this.setState({
users: nextProps.users
});
}
filterList(event) {
let users = this.state.users;
users = users.filter(function(user){
//unsure what to do here
});
this.setState({users: users});
}
render(){
const userList = this.state.users.map((user) => {
return <li>{user.name} {user.age}</li>;
});
return(
<input type="text" placeholder="Search" onChange={this.filterList}/>
<ul>
{ userList }
</ul>
);
}
}
If you want to filter for name you can use .filter together with .startsWith or .indexOf to return true or false for a given user.
You've also set a new list of users on the onChange event, which results in an empty array sooner or later. Here I've used the user state that is only changed by the props, and a filteredUsers that is changed when a keystroke happened.
class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
users: this.props.users,
filteredUsers: this.props.users,
q: ''
};
this.filterList = this.filterList.bind(this);
this.onChange = this.onChange.bind(this);
}
componentWillReceiveProps(nextProps) {
this.setState(
{
users: nextProps.users,
filteredUsers: nextProps.users
},
() => this.filterList()
);
}
onChange(event) {
const q = event.target.value.toLowerCase();
this.setState({ q }, () => this.filterList());
}
filterList() {
let users = this.state.users;
let q = this.state.q;
users = users.filter(function(user) {
return user.name.toLowerCase().indexOf(q) != -1; // returns true or false
});
this.setState({ filteredUsers: users });
}
render() {
const userList = this.state.filteredUsers.map(user => {
return <li>{user.name} {user.age}</li>;
});
return (
<div>
<input
type="text"
placeholder="Search"
value={this.state.q}
onChange={this.onChange}
/>
<ul>
{userList}
</ul>
</div>
);
}
}
const userList = [
{
name: 'Raul',
age: 29
},
{
name: 'Mario',
age: 22
}
];
ReactDOM.render(<Test users={userList} />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
You need one more state variable to store the search result, initialise that variable by same data, once user type anything store the filtered data in that, Try this:
let users = [
{
name: 'Raul',
age: 29
},
{
name: 'Mario',
age: 22
}
];
class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
users: users,
result: users,
};
this.filterList = this.filterList.bind(this);
}
componentWillReceiveProps(nextProps) {
this.setState({
users: nextProps.users,
});
}
filterList(event) {
let value = event.target.value;
let users = this.state.users, result=[];
result = users.filter((user)=>{
return user.name.toLowerCase().search(value) != -1;
});
this.setState({result});
}
render(){
const userList = this.state.result.map((user) => {
return <li>{user.name} {user.age}</li>;
});
return(<div>
<input type="text" placeholder="Search" onChange={this.filterList}/>
<ul>
{userList}
</ul>
</div>
);
}
}
ReactDOM.render(<Test/>, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='app'></div>
let datas =[
{
"id":1,
"title":"Ucat",
"handle":"mi"
},
{
"id":2,
"title":"Acat",
"handle":"csk"
},
{
"id":3,
"title":"Vcat",
"handle":"kkr"
},
{
"id":4,
"title":"Atar",
"handle":"pkbs"
}];
const [search, setSearch] =useState(datas);
const handleInputChange = (e) => {
var dm = e.target.value;
var str =dm.toString();
var debug = datas.filter(x=> x["title"].toLowerCase().includes(str));
setSearch(debug);};
<input type="text" onChange={handleInputChange}/>
{search.map((item)=>(
<div key={item.id}>
<ul>
<li>
{item.handle}
<br/>
{item.title}
</li>
</ul>
</div>
))};
If you want to filter the list, you must have a criteria to filter against. For instance you have a variable let filterAge = 18;
In that case you can filter the userlist against that value using
let users = users.filter(function(user){
//return all users older or equal to filterAge
return user.age >= filterAge;
});
Every result from the function that equals true, will be added to the users variable. A false return will leave it out. How you determine true / false is up to you. Hardcode it (not very useful), a simple equals statement (as above) or a function doing higher math. Your choice.
It always worked well for me that way.
const [data, setData] = useState([{name: 'Kondo'}, {name: 'Gintoki'}])
const handleFilterFilesTable = () => {
return data
.filter((element) => {
if (searchText !== "") {
return element?.name?.toLowerCase()?.includes(searchText?.toLowerCase())
}
return element
})
}
// ...
return (
//...
<Table data={handleFilterFilesTable()} />
)
That way I can apply multiple filters working together simply by extending the filter method like this
// ...
return data.filter().filter().filter()//...
// ...

Categories