Search Filter in React/JS - javascript

I'm using React to map through a series of data to be displayed in cards on a page. What I'm trying to do is implement search/filter functionality so that when the user types in "bicep", they only see the cards that contain the word "bicep". Ideally I would like to search the entire card, including the description. No matter what I try, I can't get the search bar to have any effect on the cards. I don't get any error messages, but when I search for anything, none of the cards are filtered out, even if they don't contain the search term.
Here is the CodeSandbox link. The search function I'm attempting to use is below:
function mySearchFunction() {
var input, filter, div, dl, a, i, txtValue;
input = document.getElementById("myInput");
filter = input.value.toUpperCase();
div = document.getElementById("myDiv");
dl = div.getElementsByTagName("dl");
for (i = 0; i < dl.length; i++) {
a = dl[i].getElementsByTagName("span")[1];
txtValue = a.textContent || a.innerText;
if (txtValue.toUpperCase().indexOf(filter) > -1) {
dl[i].style.display = "";
} else {
dl[i].style.display = "none";
}
}
}
I'm fairly new to Javascript so if there is another function/feature that you would recommend, I'm probably just not aware of it yet, and I'm open to anything, as long as it uses hooks instead of classes, and keep in mind I'm still learning this so I'm trying to take things one step at a time (i.e. I don't know anything about Angular or Vue or other non-React front-end frameworks).

You can achive that this way for example:
Attach onChange handler on input which set stateHook filter, after every change of state render method is called.
Emojies are rendered with filter(u must handle Case Insensitive Search here etc..) and they are mapped to ur object.
Here is code on CodeSandbox
function App() {
const [filter, setFilter] = useState("");
return (
<div>
<h1>
<span>emojipedia</span>
</h1>
<input
type="text"
id="myInput"
onChange={e => setFilter(e.target.value)}
placeholder="Search for names.."
/>
<div id="myDiv">
<dl className="dictionary">
{emojipedia
.filter(emoji => {
return (
emoji.name.includes(filter) || emoji.meaning.includes(filter)
);
})
.map(createEntry)}
</dl>
</div>
</div>
);
}

You need to make the following changes in the code to work .
You need to add the state in the component and make use of useState .
I added the onChange eventHandler to handle the input change.
Rewritten the filter method to filter the data based on the name property. You can make it generic by passing the property on what you want to filter the data.
Binded the state variable emojis , in the JSX , so that it re-renders the component when you change the state.
Here is the working solution: https://codesandbox.io/s/emoji-site-nc81j?file=/src/components/App.jsx
See the code below
function App() {
const [emojis, setEmojis] = useState(emojipedia);
function mySearchFunction(event) {
var tempData = emojipedia.slice();
tempData = tempData.filter(
data => data.name.indexOf(event.target.value) > -1
);
setEmojis(tempData);
}
function createEntry(emojiTerm) {
return (
<Entry
key={emojiTerm.id}
emoji={emojiTerm.emoji}
name={emojiTerm.name}
description={emojiTerm.meaning}
/>
);
}
return (
<div>
<h1>
<span>emojipedia</span>
</h1>
<input
type="text"
id="myInput"
onChange={mySearchFunction}
placeholder="Search for names.."
/>
<div id="myDiv">
<dl className="dictionary">{emojis.map(createEntry)}</dl>
</div>
</div>
);
}
export default App;

import React, {useState} from "react";
import Entry from "./Entry";
import emojipedia from "../emojipedia";
function App() {
const [results, setResults] = useState([])
function mySearchFunction(value) {
const results = emojipedia.filter(emoji => emoji.name.replace(/ /g,'').toLowerCase().includes(value.toLowerCase()))
setResults(results)
}
return (
<div>
<h1>
<span>emojipedia</span>
</h1>
<input
type="text"
id="myInput"
onChange={e => mySearchFunction(e.target.value)}
placeholder="Search for names.."
/>
<div id="myDiv">
<dl className="dictionary">
{
results.length === 0 ? 'no results' : results.map(result => {
return <Entry key={result.id}
emoji={result.emoji}
name={result.name}
description={result.meaning} />
})
}
</dl>
</div>
</div>
);
}
export default App;

Related

can't append h1 element to parent div in React?

i'm creating a simple react website that's supposed to do some calculations and find out Joules of my input values after the calculations...right now the input values are already preset but i will remove the value="" from my <input> later.
here is the .JSX component file that's the issue...one of the components.
import React, { Component } from 'react';
import Atom_icon from './cartridges.png';
class Joule_calc extends Component {
render(){
return (
<div className='Joule_div'>
<h3 style={{color:"white", textAlign:"center"}}>JOULE CALCULATOR</h3>
<label className='lab1'>WEIGHT=/GRAMS</label><br></br>
<input className='weight_inp' type='text' value="2" />
<label className='lab2'>SPEED=M/S</label><br></br>
<input className='speed_inp' type='text' value="5" />
<button className='count_button' onClick={this.Create_response}>CALCULATE</button>
<h1 className='Result_joule'></h1>
</div>
)
}
Create_response(){
console.log("creating response...")
let sum = document.createElement("h1")
sum.className = 'Result_joule'
sum.textContent = "678"
let div_panel = document.getElementsByClassName("Joule_div")
div_panel.append('Result_joule')
}
Returned_values(){
let weight_val = document.getElementsByClassName("weight_inp")[0].value;
let speed_val = document.getElementsByClassName("speed_inp")[0].value;
let final_calculation = weight_val * speed_val
return final_calculation
}
}
export default Joule_calc
so when i run my code i get
Uncaught TypeError: div_panel.append is not a function
at Create_response (Joule_calc_window.jsx:31:1)
i don't get why i can't append my new element to the div. it says it's not a function so what's the solution then? i'm new to React and web so probably it's just a noobie thing.
also i tried directly creating a h1 inside the 'Joule_div' like this.
<h1 className='Result_joule'>{"((try returning here from one of these methods))"}</h1>
but that of course failed as well. So would appreciate some help to get what's going on. i'm trying to add a number after the button click that's in h1 and in future going to be a returned number after calculating together the input values in a method.i imagine that something like
MyMethod(){
value = values calculated
return value
}
and later grab it with this.MyMethod
example
<h1>{this.MyMethod}</h1>
this is a example that of course didn't work otherwise i wouldn't be here but at least gives you a clue on what i'm trying to do.
Thank you.
You don't leverage the full power of react. You can write UI with only js world thanks to JSX. State changes triggering UI update.
I may miss some specificaiton, but fundamental code goes like the below. You should start with function component.
// Function component
const Joule_calc = () =>{
// React hooks, useState
const [weight, setWeight] = useState(0)
const [speed, setSpeed] = useState(0)
const [result,setResult] = useState(0)
const handleCalculate = () =>{
setResult(weight*speed)
}
return (
<div className="Joule_div">
<h3 style={{ color: 'white', textAlign: 'center' }}>JOULE CALCULATOR</h3>
<label className="lab1">WEIGHT=/GRAMS</label>
<br></br>
<input className="weight_inp" type="text" value={weight} onChange={(e)=>setWeight(parseFloat(e.target.value))} />
<label className="lab2">SPEED=M/S</label>
<br></br>
<input className="speed_inp" type="text" value={speed} onChange={(e)=>setSpeed(parseFloat(e.target.value))} />
<button className="count_button" onClick={handleCalculate}>
CALCULATE
</button>
<h1 className='Result_joule'>{result}</h1>
</div>
)
}
export default Joule_calc;
div_panel is an collection of array which contains the classname ["Joule_div"]. so first access that value by using indexing . and you should append a node only and your node is "sum" not 'Result_joule' and you should not use textcontent attribute because you will be gonna definitely change the value of your result as user's input value
Create_response(){
console.log("creating response...")
let sum = document.createElement("h1")
sum.className = 'Result_joule'
//sum.textContent = "678"
let div_panel = document.getElementsByClassName("Joule_div")
div_panel[0].append('sum')
}
if any problem persists , comment below

how can I make div tags as much as number in state(react.js)

I'm developing with React.js and below is a simplified version of my component. As you can see, when I click the button state(number) would get a number. And here is what I want, make div tags as much as a number in state(number) in the form tag(which its class name is 'b').
Could you tell me how to make this possible? Thanks for reading. Your help will be appreciated.
//state
const[number,setNumber] = useState('')
//function
const appendChilddiv =(e)=>{
e.preventDefault()
setNumber('')
}
<div>
<form className="a" onSubmit={appendChilddiv}>
<input
value={number}
onChange={(e)=>setNumber(e.target.value)}
type="number"/>
<button type="submit">submit</button>
</form>
<div>
<form className="b">
</form>
</div>
</div>
I've created a codesandbox which includes the following code, previously you were storing the value as a string, it'd be best to store it as number so you can use that to map out, which I do via the Array() constructor (this creates an array of a fixed length, in this case the size of the divCount state - when we update the state by changing the input value this creates a re-render and thats why the mapping is updated with the new value)
import "./styles.css";
import * as React from "react";
export default function App() {
//state
const [divCount, setDivCount] = React.useState(0);
//function
const appendChilddiv = (e) => {
e.preventDefault();
// Submit your form info etc.
setDivCount(0);
};
return (
<div>
<form className="a" onSubmit={appendChilddiv}>
<input
value={divCount}
onChange={(e) => setDivCount(Number(e.target.value))}
type="number"
/>
<button type="submit">submit</button>
</form>
<div>
<form className="b">
{Array(divCount)
.fill(0)
.map((x, idx) => (
<div key={idx}>Div: {idx + 1}</div>
))}
</form>
</div>
</div>
);
}
You can map over an array whose length is same as that entered in input & create divs (if number entered is valid):
{!isNaN(number) &&
parseInt(number, 10) > 0 &&
Array(parseInt(number, 10))
.fill(0)
.map((_, idx) => <div key={idx}>Hey!</div>)
}
isNaN checks is number is valid
parseInt(number, 10) converts string into number,
Array(n) creates a new array with n elements (all empty) (try console logging Array(5)) - so you need to fill it
.fill(n) fill the array (make each element n)
and map is used to render different elements from existing things
So, in this way, you can achieve the mentioned result.
Here's a link to working Code Sandbox for your reference
You can do the following.
First thing it does is creates a new array of some length number.
Next, it fills that array with undefined, because creating new arrays like this doesn't really create an array of that length.
Lastly, we map over this array, we use the index as our key.
We return an empty div for each item in the array.
Note, using an index as a key isn't the best idea. In general it should be something as unique as possible. If you have data you can use that is unique, then you should use that as a key.
return new Array(number).fill(undefined).map((_, key) => <div key={key}></div>);
You can even do it without 'Submit' button. See the codesandbox link and the code snippet below:
import "./styles.css";
import * as React from 'react';
import { useState } from 'react';
export default function App() {
const [divTags, setDivTags] = useState([])
const appendChilddiv = (e) => {
const numberAsString = e.target.value;
let numberDivTags;
if (numberAsString.length === 0) {
numberDivTags = 0
} else {
numberDivTags = parseInt(numberAsString, 10)
}
setDivTags([...Array(numberDivTags)])
console.log(divTags)
}
return (
<>
<form className="a">
<input type="number" onChange={appendChilddiv}/>
</form>
<form className="b">
{divTags.map((e, i) => {
return <p key={i}>Div number {i}</p>
})}
</form>
</>
);
}

How to store the return value of a function inside a variable in reactJS

my code:
import "./styles.css";
export default function App() {
getUserValue = (e) => {
let value = e.target.value;
console.log(value)
return value
}
let userInputValue = getUserValue
return (
<div>
<div>
<h4>Sign Up</h4>
</div>
<div>
<div>
<p>Username</p>
<input onChange = {getUserValue}/>
</div>
<div >
<p>Password</p>
<input/>
</div>
</div>
<div>
<button onClick = {console.log(userInputValue)}>Submit</button>
</div>
<div>
<button>
Close
</button>
</div>
</div>
);
}
code sandbox: https://codesandbox.io/s/dazzling-sea-my5qm?file=/src/App.js:0-720
I'm trying to store the returned value of "getUserValue" function to "userInputValue" variable so I can log the input the user made and use it in different functions. I can't get it to work though, when I console log the variable hoping to get the returned result after I made an input I don't get anything, as if the button doesn't work.
I'm trying to store the returned value of "getUserValue" function to "userInputValue" variable so I can log the input the user made and use it in different functions.
You'd do that by making the input state in your component. In a function component like yours, that means using the useState hook (or various libraries like Redux that have alternative ways of doing it).
Here's a simple example, but you can find lots of more complex ones by searching:
const { useState } = React;
function Example() {
// Call the hook to get the current value and to
// get a setter function to change it. The default
// value ("" in this example) is only used the first
// time you call the hook in this component's lifecycle
const [userInput, setUserInput] = useState("");
// Handle changes in the input by setting state.
// Note that this function is recreated every time your
// component function is called to update. That's mostly
// fine, but there are times you might want to optimize
// that.
const onChange = (event) => {
setUserInput(event.currentTarget.value);
};
// Handle clicks on the button that show' the current input.
const onClick = () => {
console.log(`The current userInput is "${userInput}"`);
};
// Return the rendering information for React
return <div>
{/* Provide the value and hook the "change" (really "input") event */}
<input type="text" value={userInput} onChange={onChange} />
<input type="button" onClick={onClick} value="Show Current" />
</div>;
}
ReactDOM.render(<Example />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>

How to add an array to an empty array in React using hooks

I'm trying to create a random name generator where a user would input a bunch of names in a text box, and then it would output a single name from the array.
So far I have the following:
function App() {
const [firstNames, setFirstNames] = useState(['']);
const submitResults = (e) => {
e.preventDefault();
console.log(firstNames.length);
};
return (
<main>
<form onSubmit={submitResults}>
<div className="container">
<NameField
htmlFor="first-selection"
id="first-selection"
title="Selection 1"
value={firstNames}
onChange={(e) => setFirstNames(e.target.value)}
/>
</div>
</form>
</main>
);
}
But when I console.log the firstNames.length, I'm getting the character number instead. For example, if I submit [hello, there], I'll get 12 as the firstNames.length instead of 2. I tried playing around with the onChange, but I'm still not sure how to update the firstNames state so it adds the array properly.
You've entered a string of comma separated names, so when you want to process this as an array you need to convert the string into an array of strings.
Use String.prototype.split to split the firstNames state by "," to get an array.
firstNames.split(',').length
function App() {
const [firstNames, setFirstNames] = useState("");
const submitResults = (e) => {
e.preventDefault();
console.log(firstNames.split(",").length);
};
return (
<main>
<form onSubmit={submitResults}>
<div className="container">
<input
htmlFor="first-selection"
id="first-selection"
title="Selection 1"
value={firstNames}
onChange={(e) => setFirstNames(e.target.value)}
/>
<button type="submit">Check Names</button>
</div>
</form>
</main>
);
}

Create elements dynamically within React component

I have created a helper function that creates elements dynamically within my component when I click a button. However it's not displaying half of the html I'm trying to append to the parent div.
It adds the label correctly as html, but the rest is just in plain text. Can anyone see why?
The function used to dynamically create content:
function addElement(parentId, elementTag, elementId, html) {
let parentElement = document.getElementById(parentId);
let elementToAdd = document.createElement(elementTag);
elementToAdd.setAttribute('id', elementId);
elementToAdd.innerHTML = html;
parentElement.appendChild(elementToAdd);
}
My function within my component:
static addMatch() {
let html = "<div className=\"form-group\"><label className=\"control-label\">Add Match</label>" +
"<DatePickerselected={this.state.startDate}onChange={this.handleChange.bind(this)}/></div>";
addElement('fixture-parent', 'newMatch', uuid(), html);
}
My full react component is below:
import React, {Component} from "react";
import DatePicker from "react-datepicker";
import {addElement} from "../../helpers/DynamicElementsHelper";
import moment from "moment";
const uuid = require('uuid/v1');
require('react-datepicker/dist/react-datepicker.css');
class Fixtures extends Component {
constructor() {
super();
Fixtures.addMatch = Fixtures.addMatch.bind(this);
this.state = {
startDate: moment()
};
}
handleChange(date) {
this.setState({
startDate: date
});
}
static addMatch() {
let html = "<div className=\"form-group\"><label className=\"control-label\">Add Match</label>" +
"<DatePicker selected={this.state.startDate} onChange={this.handleChange.bind(this)} /></div>";
addElement('fixture-parent', 'newMatch', uuid(), html);
}
render() {
return (
<div className="tray tray-center">
<div className="row">
<div className="col-md-8">
<div className="panel mb25 mt5">
<div className="panel-heading">
<span className="panel-title">Fixtures</span>
<p>A list of fixtures currently on the system, pulled in via ajax from Ratpack</p>
</div>
<div className="panel-body p20 pb10">
<div id="fixture-parent" className="form-horizontal">
<div className="form-group">
<label className="control-label">Add Match</label>
<DatePicker
selected={this.state.startDate}
onChange={this.handleChange.bind(this)}/>
</div>
</div>
</div>
<button onClick={Fixtures.addMatch }>Add Match</button>
</div>
</div>
</div>
</div>
);
}
}
export default Fixtures;
In React, it is recommended to not interact with the DOM directly. Instead, you should modify the JSX depending on data that you have. For your code, instead of adding an HTML tag with data from the state that changes, you should change the state and display information based on that:
addMatch() {
//Add a start date to the list of starting dates
this.setState({
listOfStartDates: [...this.state.listOfStartDates, newDate]
});
}
render(){
//For each starting date, generate a new 'match' as you called them
// note: this is the same as the stringyfied HTML tag OP had in the question
let listOfMatches = this.state.listOfStartDates.map((date)=>{
return (
<div className="form-group">
<label className="control-label">
Add Match
</label>
<DatePicker selected={date} onChange={this.handleChange.bind(this)} />
</div>
);
/* then in here your returned JSX would be just as OP originally had, with the exception that you would have {listOfMatches} where OP used to have the inserted list of HTML tags */
return //...
}
Since the component will re-render every time the state changes, the component will always have as many matches as you have starting dates.
Hope that helps!
Put a space between DatePicker and selected.
"<DatePicker selected={this.state.startDate}onChange={this.handleChange.bind(this)}/></div>";

Categories

Resources