React JS search in Array (character by character) - javascript

This is my first program in React. I've as below:
import React from 'react';
import './App.css';
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
name : ''
}
}
render() {
return (
<>
<div>
<label for="searchEmp">Search Person: </label>
<input type="text" value={this.state.name} id="searchEmp"
placeholder="Enter Person's Name"
onChange={event => this.setState({name: event.target.value})}/><br/>
</div>
{["John","Steve","Alen","Stephen",
"Smith","Alex","Jack","Andy","Jacky"].map(item => {
return <div>{item}</div>})}
</>
);
}
}
export default App;
In Output, I've something like as
I want to filter this list character by character. For e.g. When I enter S the list should filtered with names starting from S as below:
Next, If I enter t after S the list should contain only names as:
and so on. How can I get this? Apart, as a newbie to React, Is my code okay? Thanks in advance.

So, first of all I think the good practice would be ti either keep the list as constant one if it's fixed or some variable at class level,
Scenario you are asking for is preety much like you want to filter out the list each time, so you can filter out the list kept in variable using a function & could return the list from the function to use
let namesList = ['ab', 'fg', 'test'];
input = 'a';
let rgxp = new RegExp(input, "g");
function findFilterNames() {
return namesList.filter(x => x.match(rgxp));
}
test = findFilterNames();
console.log(test);

I'll try my best to answer but apologies in advance if this doesn't work, I've just started to learn React as well.
Whenever you use setState, the component will re-render itself, so keeping that in mind, you could use the following:
Create a function that looks for this.state.name and checks to see if its blank or has an actual value. If it does have a value, it will either use filter or map to run through the name array and return div elements with the values placed inside.
See code below:
class App extends React.Component {
constructor(props) {
super(props)
this.state = { name: '' };
}
renderNames() {
this.names =["John","Steve","Alen","Stephen","Smith","Alex","Jack","Andy","Jacky"];
if(this.state.name !== '') {
return this.names.map((name) => {
if (name.includes(this.state.name)){
return <div>{name}</div>;
}
});
} else {
return this.names.map((name) => {
return <div>{name}</div>;
});
}
}
render() {
return (
<>
<div>
<label for="searchEmp">Search Person: </label>
<input type="text" value={this.state.name} id="searchEmp"
placeholder="Enter Person's Name"
onChange={event => this.setState({name: event.target.value})}/><br/>
</div>
{this.renderNames}
</>
);
}
}

I will keep it simple and easy, you must use a function in Javascript called filter() with includes() function.
I will assume the search term that coming from the input called term, and the array that you need to filter it called names
const names = ["John","Steve","Alen","Stephen","Smith","Alex","Jack","Andy","Jacky"];
let term = "s";
const searchResults = names.filter(name => {
return name.toLowerCase().includes(term.toLowerCase());
});
// the result will be array that contain the following ["Steve","Stephen","Smith"]
when you consloe.log(searchResults) you will have ["Steve","Stephen","Smith"] because term value is "s" so you get the all names that have "s" character.
We change the array items to lowercase using toLowerCase() to avoid the case-senstive if there is character upper case
you can test the code here just put the name of the filtered searchResults at the end of the code
I see this way as the fastest way to search by character.
this is a repo as an example using Reactjs :
https://github.com/Tawfeekamr/react-search-in-array-character-by-character.git

Related

React put query string into form's value attribute

I have a search page, something/search?q=foo, whenever im at this page I want to put foo in my form's value tag. (Not for search purposes, I have a fully functioning search bar, I just want to show the client the last thing he searched for).
I've gotten the search term with: (window.location.href.indexOf("?q") != -1) ? window.location.href.substring(window.location.href.indexOf("?q") + 3) : '', this works, although when putting it into the forms value tag, react blocks immediately, it doesn't let me write anything into the input field. I think this is because it updates to this string super fast and you don't see it happening.
How would I achieve this, to update my form's value one time and thats it?
Here is my simplified code:
<input type="search" name="q" id="q" value={(window.location.href.indexOf("?q") != -1) ? window.location.href.substring(window.location.href.indexOf("?q") + 3) : ''} <--- This is where I want to put the search string
What i've tried so far is this:
this.state = {
value:''
}
...
handleTitle = (s) => {
this.setState({value:s})
}
...
<input ... value={this.state.value} onChange={this.HandleTitle((window.location.href.indexOf("?q") != -1) ? window.location.href.substring(window.location.href.indexOf("?q") + 3) : '')}
this results in infinite state updates
I would suggest you get the value of the search-param when the component mounts, and store it in the component's local state. Then read/update the value from state. Something like:
const [search, setSearch] = useState("");
useEffect(() => {
setSearch(new URLSearchParams(new URL(window.location.href).search).get('q'));
}, []);
return (
<intput type="text" value={search} onChange={e => setSearch(e.target.value)} />
);
I've not tested it, but you get the gist of it.
Anyway if you want to access the q natively.
Working example
https://8zwht.csb.app/?q=test
import React from "react";
import "./styles.css";
class App extends React.Component {
state = {
value: ""
};
componentDidMount() {
const search = new URL(window.location).searchParams;
const term = search.get("q");
if (term)
this.setState({
value: term
});
}
handleChange = (e) => {
this.setState({
value: e.target.value
});
};
render() {
return (
<input
type="text"
onChange={this.handleChange}
value={this.state.value}
/>
);
}
}
export default App;
It would be easier to provide a more concrete answer if you share the code or gist

Searching through multiple properties in an object using only one input aka global search

I am trying to build an universal/global search for an object in reactjs using only one text input.
I tried different approaches, including converting the string into an array and then use it to filter through my object. I managed to make the input search through all the properties, but only one at the time.
const data = [
{
name:"Italy",
title:"Best place for Pizza"
},
{
name:"USA",
title:"It is a federal state"
}
]
export class MyApp extends React.Component{
constructor(props){
super(props);
this.state = {
filteredData:[],
filter:''
}
this.handleSearch.bind(this);
}
componentDidMount(){
this.setState({
filteredData:data
})
}
handleSearch = (e) =>{
const filter = e.target.value;
this.setState({
filter
})
}
render(){
var filteredData = data;
var searchValue = this.state.filter;
filteredData = filteredData.filter(country => {
return['name', 'title'].some(key =>{
return country[key].toString().toLowerCase().includes(searchValue)
})
})
return (
<div>
<input type="search" onChange={(e) => this.handleSearch(e)}/>
{
filteredData.map(result =>{
return <p>{result.name} | {result.title}</p>
})
}
</div>
);
}
What I want is to be able to combine properties. For example: I want to be able to type in "Italy best place for..." and still get a result. I don't want to be limited to only type either "Best place for Pizza" or "Italy" to get the entry.
You can make your search do something like this:
const data = [ { name:"Italy", title:"Best place for Pizza" }, { name:"USA", title:"It is a federal state" } ]
let search = (arr, str) => {
return arr.filter(x => Object.values(x)
.join(' ')
.toLowerCase()
.includes(str.toLowerCase()))
}
console.log(search(data, 'Italy best'))
console.log(search(data, 'USA it is'))
The idea is to use Array.filter and inside get the values of the object (using Object.values) (here we assume they all are strings) and combine them (via Array.join) into one string. Then use Array.includes to search inside of it.
I'm not sure if this is best practice or not, but when I ran into a similar problem I just kept chaining my .filter() functions to each other, with each filter searching through a different property.
filteredData = filteredData.filter(country => {
return country['name'].toString().toLowerCase().includes(searchValue)
}).filter(country => {
return country['title'].toString().toLowerCase().includes(searchValue)
})
It works, but doesn't look as pretty.

Changing state but then clearing input React

I'm writing my first React app and really struggling to do something quite basic.
I have an Input component that has an array in state, which, when it has two numbers, it sends them and a unique ID as an object up to a parent Component which stores the object in an array.
This is all fine and I can do it. The problem is clearing the inputs afterwards.
So far as I understand it, I need the value of the inputs to be stored in the Component state (in the array) when I do an on Change. Those values are then used for submitting the form.
However, if the inputs are getting their value from state, they need to have a value on render, which I don't want. I only want them to have a value after I've entered something into the input. I've tried using setState to replace the inputTable with an empty array after submission, but that's still not changing the values.
Here's the code - to reiterate, I want to find a way to just clear the inputs after I've submitted the array. At the moment it keeps saying that I'm changing an uncontrolled component into a controlled one, which I understand, but I don't understand how else I'm meant
Please trust that I've tried to solve this by myself, checking out MDN docs about forms and inputs, but I'm really not getting anywhere. I'd really appreciate the help.
import React, { Component } from 'react';
class Input extends Component {
constructor(props) {
super(props)
this.state = {
inputTable: [],
uniqueId: 1
}
this.handleChange = this.handleChange.bind(this);
this.sendTables = this.sendTables.bind(this);
}
async handleChange(e) {
e.preventDefault();
await this.setState({
inputTable: [
...this.state.inputTable, e.target.value
]
})
console.log(this.state.inputTable)
// how do I handle this onChange correctly?
}
async sendTables(e) {
e.preventDefault();
this.props.submitTable(this.state.inputTable, this.state.uniqueId);
let newArray = [];
await this.setState({
inputTable: newArray,
uniqueId: this.state.uniqueId + 1
})
console.log(this.state.inputTable)
// how can I clear the inputs?
}
render() {
return (
<div className="Input">
<form action="" onSubmit={this.sendTables}>
<input required type="number" name="value0" placeholder="a number..." onChange={this.handleChange} value={this.state.inputTable[0]} />
<span>X</span>
<input required type="number" name="value1" placeholder="multiplied by..." onChange={this.handleChange} value={this.state.inputTable[1]}/>
<input type="submit" value="Add times table" />
</form>
</div>
);
}
}
export default Input;
Parent component:
import React, { Component } from 'react';
import Input from './Input/Input';
class InputTimesTables extends Component {
constructor() {
super();
this.state = {
tables: [],
noInputs: 1
}
this.pushToTables = this.pushToTables.bind(this);
}
addInput() {
// this will be used to add an additional input
// it will increment no. inputs
}
async pushToTables(arr, id) {
let newTimesTable = {
timesTable: arr,
uniqueId: id
}
await this.setState({
tables: [...this.state.tables, newTimesTable]
})
// console.log(`The ITT state looks like:`, this.state.tables);
//this will take the two numbers in the array from the Input
// and push them to the tables array
// it won't run unless there are two numbers in that array
console.log('The main state array now looks like this: ', this.state.tables)
}
// clearTables(id){
// console.log(`splicing array no ${id}`);
// let newArray = this.state.tables;
// newArray.splice(id, 1);
// this.setState({
// tables: newArray
// })
// console.log(this.state.tables);
// // console.log(`The ITT state looks like:`, this.state.tables);
// }
render() {
return (
<div>
<Input submitTable={this.pushToTables}/>
<h3>Currently being tested on:</h3>
<ul>
</ul>
</div>
);
}
}
export default InputTimesTables;
Many thanks.
I think I've got it - the issue is that a number input is not actually a number - it's a string. So I can just set the arrays to ["", ""] and then reset them, and there's no problem. I hope this helps someone else if they run into it.
Thanks if you had a look!

Double setState method in one function

I am trying to create a autocomplete component. It's an input where user types the countru name and if letters match name of some country, the hints are displayed.
In my App Component i have method handleChange Within this method i change my state two times, which is bad idea.
How can I split it to change state in distinct methods ?
import React, { Component } from 'react';
import AutoComplete from './autoComplete.jsx';
import data from './data.json';
class App extends Component {
constructor(props) {
super(props);
this.state = {
inputValue: '',
resoults: []
}
}
handleChange() {
let inputValue = this.refs.input.value;
this.setState({
inputValue: inputValue
});
let regular = "^" + this.state.inputValue;
let reg = new RegExp(regular , "i");
let filtered = data.filter((i,index)=> {
return (reg.test(i.name)
);
});
console.log(filtered);
this.setState({resoults:filtered})
}
render() {
return (
<div>
<input onChange={this.handleChange.bind(this)} type="text" ref="input"/>
<h3>You typed: {this.state.inputValue}</h3>
<AutoComplete resoults={this.state.resoults} />
</div>
);
}
}
export default App;
import React, {Component} from 'react';
class AutoComplete extends Component {
render() {
return (
<div>
<h4>autocompleteComponent</h4>
{this.props.resoults.map((i)=> {
return (
<ul>
<li>{i.name}</li>
</ul>
);
})}
</div>
);
}
}
export default AutoComplete;
I found myself in this position many times, but I got to the conclusion that it's better to compute the autocomplete options (in your case) without having them in the state of your component.
As I have used them until now, the state and props of a component should represent minimal data needed to render that specific component. Since you have your input value in the state, having the autocomplete options there also seems redundant to me. So here is what I propose:
class App extends Component {
this.state = {
inputValue: '',
};
handleChange(e) {
const inputValue = e.target.value;
this.setState({
inputValue,
});
}
computeResults() {
const {inputValue} = this.state;
// your functionality for computing results here
}
render() {
const {inputValue} = this.state;
const results = this.computeResults();
return (
<div>
<input type="text" onChange={this.handleChange.bind(this)} value={inputValue} />
<h2>You typed: {inputValue}</h2>
<Autocomplete results={results} />
</div>
);
}
}
Notes
Since your results come synchronously, via the .json import, this seems the perfect solution to me. If you want to get them via fetch or anything else, then you'll have to figure out a slightly different approach, but keep in mind that the state of your component should not contain redundant data.
Stop using ref with string value! and use refs when there is absolutely no other way because a React component should not generally deal with DOM operations directly. If you really need to use refs, use ref callbacks.
Hope this helps!
Use another function and setState callBack:
handleChange() {
let inputValue = this.refs.input.value;
this.setState(
{
inputValue: inputValue
},
() => this.secondFunc()
);
}
secondFunc() {
let regular = '^' + this.state.inputValue;
let reg = new RegExp(regular, 'i');
let filtered = data.filter((i, index) => {
return reg.test(i.name);
});
console.log(filtered);
this.setState({ resoults: filtered });
}

ES6 includes but case insensitive

I know I can loop through array and just change value toLowerCase().
I'm curious if there's a reactjs or es6 method that can check if the value is in array.
On addTodo function. You can see that I use includes but this method is case sensitive.
Here's what I have
class Todo extends React.Component {
render() {
return (
<div className="todo">
<input type="checkbox" />
<p>{this.props.children}</p>
</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.todos = [
'Get Up from bed',
'Eat Breakfast'
];
}
eachTodo(task, i) {
return (
<Todo key={i} index={i}>{task}</Todo>
)
}
addToDo() {
let task_title = this.refs.newTodo.value;
if(task_title !== '') {
let arr = this.todos;
var arr_tlc = this.todos.map((value) => {
return value.toLowerCase();
})
if(arr_tlc.indexOf(task_title.toLowerCase()) === -1) {
arr.push(task_title);
this.setState({
todos: arr
});
}
}
}
render() {
return (
<div className="main-app">
<input ref="newTodo" placeholder="Task"/>
<button onClick={this.addToDo.bind(this)}>Add Todo</button>
<div className="todos">
{this.todos.map(this.eachTodo)}
</div>
</div>
);
}
}
Any help would be appreciated. Thanks!
I would suggest using a Map instead of an array for your todo list. A Map has the advantage that it provides key-based look-up in constant time, and does not store duplicate entries with the same key. You could then use the lower case variant as the key, and the original (mixed-case) string as the value for that key.
You could define todos as a Map instead of an array, and use the lower case string as the key:
constructor(props) {
super(props);
this.todos = new Map();
this.addToDo('Get Up from bed');
this.addToDo('Eat Breakfast');
}
Then, to add the task becomes very straightforward, as a Map overwrites duplicates:
addToDo() {
this.todos.set(task_title.toLowerCase(), task_title);
this.setState({
todos: this.todos
});
}
In rendering you would need to use Array.from(..., <map-function>), as .map is not defined for Map objects:
<div className="todos">
{Array.from(this.todos, this.eachTodo)}
</div>
Which means the eachToDo method will receive a pair of strings (an array), instead of a string, and we want to display the second of the pair, so using index [1]:
eachTodo(task, i) {
return (
<Todo key={i} index={i}>{task[1]}</Todo>
)
}
No, the only built-in methods are case sensitive. You'll need that loop and either toLowerCase or a case-insensitive regular expression (perhaps tucked away in a useful reusable function).

Categories

Resources