How to filter data using react? - javascript

I have created search filter but I am not able to type anything in search input why so ? I have created searchTermChanged method but why is it not working ? When user types in input field the projects should get filtered based on title.
Code:
import Projects from '../../data/projects';
export default class Home extends Component {
constructor(props) {
super(props);
this.state = {
search: '',
projects: Projects
}
}
searchTermChanged = (event) => {
this.setState({ projects: this.state.projects.filter(val =>
val.title.toLowerCase().indexOf(this.state.search.toLowerCase()) > -1 )
})
}
render() {
return (
<div>
<div className="header">
<div className="md-form mt-0 customsearch">
<input className="form-control" type="text" placeholder="Search projects" aria-label="Search"
value={this.state.search}
onChange={e => this.searchTermChanged(e.target.value)}
/>
</div>
</div>
<div class="container-fluid">
<div class="row">
{this.state.projects.map((val,index) => (
<div class="col-3">
<Card title={val.title} by={val.by} blurb={val.blurb}
url={val.url} funded={val.funded} backers={val.backers} imgurl={index}/>
</div>
))}
</div>
</div>
</div>
)
}
}

You need to make sure you're making correct use of the state.
import Projects from '../../data/projects';
export default class Home extends Component {
constructor(props) {
super(props);
this.state = {
search: '',
projects: Projects
}
}
searchTermChanged = (search) => {
this.setState({
//Update the search state here.
search,
//Use the current search state to filter
projects: this.state.projects.filter(val =>
val.title.toLowerCase().indexOf(search.toLowerCase()) > -1 )
}
);
}
render() {
return (
<div>
<div className="header">
<div className="md-form mt-0 customsearch">
<input className="form-control" type="text" placeholder="Search projects" aria-label="Search"
value={this.state.search}
onChange={e => this.searchTermChanged(e.target.value)}
/>
</div>
</div>
<div class="container-fluid">
<div class="row">
{this.state.projects.map((val,index) => (
<div class="col-3">
<Card title={val.title} by={val.by} blurb={val.blurb}
url={val.url} funded={val.funded} backers={val.backers} imgurl={index}/>
</div>
))}
</div>
</div>
</div>
)
}
}

I think if you don't need to change the projects you can also do the bellow to simplify your logic:
constructor(props) {
super(props);
this.state = {
search: ''
}
}
render() {
let {search} from this.state;
let myProjects = projects.filter((p) => {
p.title.toLowerCase().indexOf(search.toLowerCase) > -1
});
return (
<div>
<div className="header">
<div className="md-form mt-0 customsearch">
<input className="form-control" type="text" placeholder="Search projects" aria-label="Search"
value={this.state.search}
onChange={e => this.setState({search: e.target.value})}
/>
</div>
</div>
<div class="container-fluid">
<div class="row">
{myProjects.map((val,index) => (
<div class="col-3">
<Card title={val.title} by={val.by} blurb={val.blurb}
url={val.url} funded={val.funded} backers={val.backers} imgurl={index}/>
</div>
))}
</div>
</div>
</div>
)
}

You need to user Projects variable directly to filter otherwise filter changes will search on existing state. You need to set search value to refect what is your input
searchTermChanged = (event) => {
console.log(event);
this.setState({
projects: Projects.filter(val =>
val.title.toLowerCase().indexOf(event.toLowerCase()) > -1 ),
search: event <-- here
})
}
stackblitz: https://stackblitz.com/edit/react-fyf7fr

You are not changing the state of "search".
Assuming u have an input like this:
<input type="text" id="whatever" className="whatever" onChange={(event) => props.searchTermChanged(e.target.value)} />
you can change your method searchTermChanged
searchTermChanged = (value) => {
this.setState({search: value});
this.setState({ projects: this.state.projects.filter(val =>
val.title.toLowerCase().indexOf(value.toLowerCase()) > -1 )
});
}
The reason why u use "value" instead of "this.state.search" here "indexOf(value.toLowerCase())" its because setState is asynchronous and you can reach that piece of code with state outdated. And you are sure that "value" has the right value.

Related

React How to show individual data into popup

I am learning react I want to show movie data when clicking on particular div. currently, I called fancy box which is not right method to get the result
So I need help to show movie data once click on particular div.
class App extends React.Component {
constructor() {
super();
this.state = {
data: [],
search: '',
};
}
updateSearch(event) {
this.setState({search: event.target.value.substr(0, 20)});
}
componentDidMount() {
fetch('http://www.omdbapi.com/?apikey=MyKey&s=fast&plot=full')
.then((Response) => Response.json())
.then((findresponse) => {
console.log(findresponse);
this.setState({
data: findresponse.Search,
});
});
}
render() {
let filteredMovie = this.state.data.filter((dynamicData) => {
return dynamicData.Title.toLowerCase().indexOf(this.state.search.toLowerCase()) !== -1;
});
return (
<div className="container movies_list">
<div className="row">
<div className="col-md-12 p-4">
<form>
<input
type="text"
className="form-control"
placeholder="Search"
value={this.state.search}
onChange={this.updateSearch.bind(this)}
/>
</form>
</div>
{filteredMovie &&
filteredMovie.map((dynamicData, key) => (
<div className="col-md-3 mb-3" key={key}>
<div className="card">
<img src={dynamicData.Poster} className="card-img-top" alt="..." />
<div className="card-body">
<h6 className="card-title">{dynamicData.Title} </h6>
<h6 className="card-title">Year: {dynamicData.Year} </h6>
<p className="card-text">{dynamicData.Plot} </p>
<a
data-fancybox
data-src="#hidden-content"
href="javascript:;"
className="btn btn-info"
>
View
</a>
<div id="hidden-content">
<img src={dynamicData.Poster} className="card-img-top" alt="..." />
<h2>{dynamicData.Title}</h2>
<p>{dynamicData.Year}</p>
</div>
</div>
</div>
</div>
))}
</div>
</div>
);
}
}
I highly recommend Reakit for modal & popovers.

My component 'Recipe Items' is being rendered in a single column, when the correct one for each row is 5 columns

I have to render a component from an .json file, until then okay, to be able to read and pass the api values ​​to my component ('RecipeItem'). The problem lies in the part of rendering, because the correct one would be the components being in 5 columns instead of only one.
enter image description here
updated codes below !!!
File RecipeItem.js
const RecipeList = ({ searchString }) => {
return(
<div>
{console.log('to aqui')}
<img className="card-img-top img-fluid" src={searchString.thumbnail} alt={searchString.title} />
<div className="card-body">
<h5 className="card-title">{searchString.title}</h5>
<p className="card-text">
<strong>Ingredients: </strong>{searchString.ingredients}
</p>
</div>
</div>
)
}
const RecipeItem = (props) => {
return (
<div className="col-sm-3 mt-4">
<div className="card">
{props.list && props.list.map((searchString, index) =>
<RecipeList searchString={searchString} key={index} />
)}
</div>
</div>
)
}
File App.js
class App extends Component {
constructor(props) {
super(props);
this.state = {
searchString: []
};
}
componentDidMount() {
this.setState({ searchString : data.results })
}
render() {
return (
<div className="App">
<Navbar />
<div className="container mt-10">
<div className="row">
<RecipeItem list={this.state.searchString}/>
</div>
</div>
</div>
);
}
}
Is this working ?
class App extends Component {
render() {
return (
<div className="App">
<Navbar />
<div className="container mt-10">
<div className="row">
{RecipesData.results.map(recipe =>
<RecipeItem
title={recipe.title}
ingredients={recipe.ingredients}
source={recipe.href}
thumbnail={recipe.thumbnail} />
)}
</div>
</div>
</div>
);
}
}

React redux search filter not displaying filtered content?

I have created search filter using react redux but when I type in text in search field the list of projects is not changed based on value I type in the search input. Why so ? The projects should get filtered based on search input but it is not working why so ?
Code:
home.js:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { searchTermChanged } from '../../store/actions/searchAction';
import projects from '../../data/projects';
class Home extends Component {
render() {
const { searchTermChanged } = this.props;
return (
<div>
<Navbar/>
<div className="header">
<div className="md-form mt-0 customsearch">
<input className="form-control" type="text" placeholder="Search projects" aria-label="Search"
value={this.props.searchTerm}
onChange={e => searchTermChanged(e.target.value)}
/>
</div>
<div class="container-fluid">
<div class="row">
{projects.map( (val,index) => (
<div class="col-3" key={index}>
<Card title={val.title} by={val.by} blurb={val.blurb} url={val.url} funded={val.funded} backers={val.backers} imgurl={index}/>
</div>
))}
</div>
</div>
</div>
)
}
}
const mapStateToProps = state => ({
search: state.search.searchTerm
})
export default connect (mapStateToProps, { searchTermChanged })(Home);
searchReducer.js:
import { SEARCH_INPUT_CHANGED } from '../actions/types';
import Projects from '../../data/projects';
const initialState = {
searchTerm: '',
projects: Projects
}
export default function (state = initialState, action) {
switch (action.type) {
case SEARCH_INPUT_CHANGED:
const { searchTerm } = action.payload;
return {
...state,
searchTerm: searchTerm,
projects: searchTerm
? Projects.filter(
projects =>
projects.name.toLowerCase().indexOf(searchTerm.toLowerCase()) >
-1,
)
: Projects,
};
default:
return state;
}
}
searchAction.js:
import { SEARCH_INPUT_CHANGED } from './types';
export const searchTermChanged = (searchTerm) => ({
type: SEARCH_INPUT_CHANGED,
payload: { searchTerm }
});
index.js:
import {combineReducers} from 'redux';
import searchReducer from './searchReducer';
export default combineReducers({
search: searchReducer
})
Screenshot:
Working code:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { searchTermChanged } from '../../store/actions/searchAction';
class Home extends Component {
render() {
const { searchTermChanged } = this.props;
return (
<div>
<Navbar/>
<div className="header">
<div className="md-form mt-0 customsearch">
<input className="form-control" type="text" placeholder="Search projects" aria-label="Search"
value={this.props.search}
onChange={e => searchTermChanged(e.target.value)}
/>
</div>
<div class="container-fluid">
<div class="row">
{this.props.projects.map( (val,index) => (
<div class="col-3">
<Card title={val.title} by={val.by} blurb={val.blurb}
url={val.url} funded={val.funded} backers={val.backers} imgurl={index}/>
</div>
))}
</div>
</div>
</div>
)
}
}
const mapStateToProps = state => ({
search: state.search.searchTerm,
projects: state.search.projects
})
export default connect (mapStateToProps, dispatch => ({ searchTermChanged: searchTerm => dispatch(searchTermChanged(searchTerm)) }))(Home);
You import the projects in the Home Component strait from the file and not the reducer.
add projects to your mapStateToProps function
I think you didn't use the reducer with right way.
For example
{projects.map( (val,index) => (
<div class="col-3">
<Card title={val.title} by={val.by} blurb={val.blurb}
url={val.url} funded={val.funded} backers={val.backers} imgurl={index}/>
</div>
))}
is wrong.
You used projects imported from '../../data/projects'
So I recommend you to use as follows
1 : remove import statement for projects
2 : change code like this.
render() {
const { searchTermChanged } = this.props;
const {projects} = this.props.search; //added
return (
<div>
<Navbar/>
<div className="header">
<div className="md-form mt-0 customsearch">
<input className="form-control" type="text" placeholder="Search projects" aria-label="Search"
value={this.props.searchTerm}
onChange={e => searchTermChanged(e.target.value)}
/>
</div>
<div class="container-fluid">
<div class="row">
{projects.map( (val,index) => (
<div class="col-3">
<Card title={val.title} by={val.by} blurb={val.blurb}
url={val.url} funded={val.funded} backers={val.backers} imgurl={index}/>
</div>
))}
</div>
</div>
</div>
)
}
The 'searchtermChanged' action creator has to be used along with dispatch rather than being called directly. Look for reference -
https://redux.js.org/basics/actions
https://react-redux.js.org/api#connect

reactjs checkboxlist component - Updating state changes in parent

I have a bunch of checkbox-list requirements. I will explain in detail. I have a bunch of languages say:
var languages = ["English", "German", "French", "Spanish", "Mandarin", "Tamil"]
I have a parent component which has a form where I have four sections, Say:
class Page extends React.Component {
render() {
return (
<form>
<h1>CanSpeak</h1> <chkboxlist someProp="speak" />
<h1>CanSpeak</h1> <chkboxlist someProp="read" />
<h1>CanSpeak</h1> <chkboxlist someProp="write" />
<h1>CanSpeak</h1> <chkboxlist someProp="understand" />
<button
onClick={e => {
console.log("Need the various chkboxlist values here");
e.preventDefault();
}}
>
Save
</button>
</form>
);
}
}
I want the chkboxlist component to keep track of the list of selected languages in each section and make them available in the "Save" button click handler. I wish to keep track of the state changes (list of selected languages under each section) in the "Page" component.
I do not want to use redux or some such external state management.
Now what is the way to create this chkboxlist component such that the state changes can be tracked in the parent Page component ? Are there any existing components which will fit this requirement and is used widely in the react ecosystem without having to reinvent the wheel ?
I don't know if pulling in a seperate component would be really useful - as it's only a really tiny piece of functionality.
Working fiddle here:
https://jsbin.com/tusakexire/edit?html,js,output
You could do something like:
class Chkboxlist extends React.Component {
constructor(props) {
super(props)
this.state = {}
props.values.map((v, i) => {
this.state[v] = false
})
}
onChange(key, value) {
this.setState({ [key]: value }, (state) => {
this.props.onChange(this.state)
})
}
render() {
return (
<div className="list-group-item form-group">
{this.props.values.map((value, i) => (
<div className="checkbox" key={i}>
<label>
<input
onChange={(e) => this.onChange(value, e.target.checked)}
type='checkbox'
value={this.state[value]}
/>
{value}
</label>
</div>
))}
</div>
)
}
}
class Page extends React.Component {
constructor(props) {
super(props)
this.state = {}
}
onChange(name, values) {
this.setState({ [name]: values })
}
render() {
const languages = ["English", "German", "French", "Spanish", "Mandarin", "Tamil"]
return (
<div className="container">
<div className="row">
<form className="form">
<div className="list-group col-xs-6">
<h4>Can Speak</h4>
<Chkboxlist
onChange={(values) => this.onChange('speak', values)}
values={languages}
/>
</div>
<div className="list-group col-xs-6">
<h4>Can Read</h4>
<Chkboxlist
onChange={(values) => this.onChange('read', values)}
values={languages}
/>
</div>
<div className="list-group col-xs-6">
<h4>Can Write</h4>
<Chkboxlist
onChange={(values) => this.onChange('write', values)}
values={languages}
/>
</div>
<div className="list-group col-xs-6">
<h4>Can Understand</h4>
<Chkboxlist
onChange={(values) => this.onChange('understand', values)}
values={languages}
/>
</div>
<button
className="btn btn-primary"
onClick={(e) => {
console.log(this.state);
e.preventDefault();
}}
>
Save
</button>
</form>
</div>
</div>
);
}
}
ReactDOM.render(<Page />, document.getElementById('app'))

Passing props from child to parent(without context)

I'm having a little bit of problem with wrapping my head around with passing states into parents. I need to send data from form container to app so that I can show updated states of list in weather info after submit
class App extends Component {
render() {
return (
<div className="App">
<div className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h2>Weather App</h2>
</div>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
<FormContainer label="Name of the city:"/>
<WeatherInfo
nameOfCity={this.state.nameOfCity}
weatherDescription={this.state.weatherDescription}
windSpeed={this.state.windSpeed}
temperature={this.state.temperature}
maxTemperature={this.state.maxTemperature}
minTemperature={this.state.minTemperature}
/>
</div>
);
}
}
export default App;
Form Container
class FormContainer extends Component {
constructor(props) {
super(props);
this.state = {
cityName: '',
nameOfCity:'',
weatherDescription:'',
windSpeed:'',
temperature:'',
maxTemperature:'',
minTemperature:''
};
this.handleFormSubmit = this.handleFormSubmit.bind(this);
this.handleCityName = this.handleCityName.bind(this);
}
handleFormSubmit(e) {
e.preventDefault();
const SendForm = {
cityName: this.state.cityName
};
console.log(SendForm);
fetch(`http://api.openweathermap.org/data/2.5/forecast/weather?q=${SendForm.cityName}&units=metric&APPID=********`)
.then(res => res.json())
.then(results => {
this.setState({
nameOfCity: results.city.name,
weatherDescription: results.list[0].weather[0].description,
windSpeed: results.list[2].wind.speed,
temperature: results.list[0].main.temp,
maxTemperature: results.list[0].main.temp_max,
minTemperature: results.list[0].main.temp_min
});
});
}
handleCityName(value) {
this.setState({ cityName: value });
}
render() {
return (
<div>
<form onSubmit={this.handleFormSubmit}>
<label>{this.props.label}</label>
<SearchBar
name="CityName"
type="text"
value={this.state.cityName}
placeholder="search"
onChange={this.handleCityName}
/>
<button type="submit"
className=""
value='Submit'
placeholder="Search" />
</form>
</div>
);
}
}
export {FormContainer};
Search bar component
const SearchBar = (props) => (
<div>
<label>{props.label}</label>
<input name={props.name} type={props.inputType} value={props.value} placeholder={props.placeholder} onChange={(e)=>props.onChange(e.target.value)}/>
</div>
);
export default SearchBar;
and Weather Info component
const WeatherInfo = (props) => (
<div>
<ul>
<li>{props.nameOfCity}</li>
<li>{props.weatherDescription}</li>
<li>{props.windSpeed}</li>
<li>{props.temperature}</li>
<li>{props.maxTemperature}</li>
<li>{props.minTemperature}</li>
</ul>
</div>
);
export default WeatherInfo;
You can pass method to update App state to FormContainer component
class App extends Component {
constructor() {
this.state = {
cityName: '',
nameOfCity:'',
weatherDescription:'',
windSpeed:'',
temperature:'',
maxTemperature:'',
minTemperature:''
};
}
updateInfo(results) {
this.setState({
nameOfCity: results.city.name,
weatherDescription: results.list[0].weather[0].description,
windSpeed: results.list[2].wind.speed,
temperature: results.list[0].main.temp,
maxTemperature: results.list[0].main.temp_max,
minTemperature: results.list[0].main.temp_min
});
}
render() {
return (
<div className="App">
<div className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h2>Weather App</h2>
</div>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
<FormContainer label="Name of the city:" updateInfo={this.updateInfo.bind(this)}
nameOfCity={this.state.nameOfCity}
/>
<WeatherInfo
nameOfCity={this.state.nameOfCity}
weatherDescription={this.state.weatherDescription}
windSpeed={this.state.windSpeed}
temperature={this.state.temperature}
maxTemperature={this.state.maxTemperature}
minTemperature={this.state.minTemperature}
/>
</div>
);
}
}
export default App;
And call it from FormComponent
class FormContainer extends Component {
constructor(props) {
super(props);
this.handleFormSubmit = this.handleFormSubmit.bind(this);
this.handleCityName = this.handleCityName.bind(this);
}
handleFormSubmit(e) {
e.preventDefault();
const SendForm = {
cityName: this.props.cityName
};
console.log(SendForm);
fetch(`http://api.openweathermap.org/data/2.5/forecast/weather?q=${SendForm.cityName}&units=metric&APPID=********`)
.then(res => res.json())
.then(results => {
this.props.updateInfo(results);
});
}
handleCityName(value) {
// Do what you want to do, like resend API request or smth
}
render() {
return (
<div>
<form onSubmit={this.handleFormSubmit}>
<label>{this.props.label}</label>
<SearchBar
name="CityName"
type="text"
value={this.props.cityName}
placeholder="search"
onChange={this.handleCityName}
/>
<button type="submit"
className=""
value='Submit'
placeholder="Search" />
</form>
</div>
);
}
}
export {FormContainer};

Categories