Having Trouble Removing Item From Array in React - javascript

Upon loading the page, data is retrieved from my API and stored in an array. It then displays a button for each object in the array and titles is appropriately.
What needs to happen next is when I click a button, that button disappears, and the associated item removed from the array. That part is not working. The button is registering the click, and the function is running. However the button does not disappear, and when I log the array again, the array appears to be unchanged. I cannot figure out why. Can anyone help me spot the issue?
Here is my code, the part in question is the "handleAddPolicy" function:
import React, { Component, Fragment } from 'react';
import PolicyButton from './PolicyButton/PolicyButton';
class Handbook extends Component {
constructor(props){
super(props);
this.state = {
clients: [],
policies: [],
client: 'Choose Client',
logo: '',
color1: '#000',
color2: '#fff'
};
}
componentDidMount() {
fetch('/api/themes/all')
.then(res => res.json())
.then((result) => {
this.setState({ clients: result });
console.log(result);
})
.then(
fetch(`/api/policy/all`)
.then(res => res.json())
.then((result) => {
this.setState({ policies: result });
console.log(result);
})
);
}
handleAddPolicy = policyId => {
console.log(`button clicked`);
const policies = this.state.policies.filter(policy => policy.id !== policyId);
this.setState({ policies: policies});
console.log(this.state.policies);
}
render(){
return(
<Fragment>
{/* <ClientPicker /> */}
<div className='buttons'>
{this.state.policies.map(policy => (
<PolicyButton key={policy.id} policy={policy.policy} onAddPolicy={this.handleAddPolicy} />
))}
</div>
</Fragment>
);
}
}
export default Handbook;
And here is code for my button that should disappear after being clicked if it helps:
import React, { Component } from 'react';
import styled from 'styled-components';
class PolicyButton extends Component {
state = {
id: this.props.id,
policy: this.props.policy
}
render(){
return(
<button onClick={() => this.props.onAddPolicy(this.props.id)}>{this.props.policy}</button>
)
}
}
export default PolicyButton;

You missed the id prop when rendering PolicyButton
<Fragment>
{/* <ClientPicker /> */}
<div className='buttons'>
{this.state.policies.map(policy => (
<PolicyButton
key={policy.id}
/* This is what you missed */
id={policy.id}
policy={policy.policy}
onAddPolicy={this.handleAddPolicy}
/>
))}
</div>
</Fragment>

Related

Generating Search suggestions in React?

I am looking to generate search suggestions that match data collected, like so:
As you type in you get suggestions:
I am referencing some of the tutorial work from WesBos:
https://github.com/wesbos/JavaScript30/blob/master/06%20-%20Type%20Ahead/index-FINISHED.html
I've got the data logging in the console but now I am unsure how to get it to render. Below are my components (My thoughts were to generate the divs as a loop in App.js and pass the props to Match.js which I would eventually import but I am not sure if I am approaching this wrong):
App.js
import React, { Component } from 'react';
import { Form, Button } from 'react-bootstrap';
import 'bootstrap/dist/css/bootstrap.min.css';
const my_data = require('./data/test.json')
class App extends Component {
constructor(props) {
super(props);
this.state = {
links: [],
selectedLink:null,
userLocation: {},
searchInput: "",
showMatches: false,
matches: []
};
}
componentDidMount() {
fetch('https://data.cityofnewyork.us/resource/s4kf-3yrf.json')
.then(res=> res.json())
.then(res=>
//console.log(json)
this.setState({links:res})
);
}
render() {
const handleInputChange = (event) => {
event.preventDefault()
this.setState({searchInput: event.target.value })
//console.log(event.target.value)
}
const handleSubmit = (event) => {
event.preventDefault()
const data = this.state
displayMatches();
}
const findMatches = (wordToMatch, my_obj) => {
return my_obj.filter(place => {
// here we need to figure out the matches
const regex = new RegExp(wordToMatch, 'gi');
//console.log(place.street_address.match(regex))
return place.street_address.match(regex)
});
}
const displayMatches =() => {
const matchArray = findMatches(this.state.searchInput, this.state.links);
matchArray.map(place => {
console.log(place.street_address);
this.setState({matches:place})
this.setState({showMatches:true})
});
}
return (
<div>
<Form style = {{width: "75%"}} onSubmit = {handleSubmit}>
<Form.Group controlId="formSearch">
<Form.Control
type="text"
name = "my_search"
placeholder="Search for a Link Near you..."
onChange = {handleInputChange} />
</Form.Group>
<Button variant="primary" type="submit">
Search
</Button>
</Form>
<div>
{`How can I generate the console logged values as dynammic suggestions?`}
</div>
</div>
);
}
}
export default App;
Match.js
import React from 'react';
const match = ( props ) => {
return (
<div className="Matches">
<p>{`data is passed: ${props.address}`}</p>
</div>
)
};
export default match;
Appreciate the help.
Answers - Using Suggestions below
App.js
import React, { Component } from 'react';
import { Form, Button, ListGroup } from 'react-bootstrap';
import 'bootstrap/dist/css/bootstrap.min.css';
import Match from './Match'
const my_data = require('./data/test.json')
class App extends Component {
state = {
links: [],
selectedLink:null,
userLocation: {},
searchInput: "",
showMatches: false,
matches: [],
searchLink:[]
}
componentDidMount() {
fetch('https://data.cityofnewyork.us/resource/s4kf-3yrf.json')
.then(res=> res.json())
.then(res=>
//console.log(json)
this.setState({links:res})
);
}
handleInputChange = (event) => {
event.preventDefault()
this.setState({searchInput: event.target.value })
console.log(event.target.value)
}
handleSubmit = (event) => {
event.preventDefault()
this.displayMatches();
}
findMatches = (wordToMatch, my_obj) => {
return my_obj.filter(place => {
// here we need to figure out the matches
const regex = new RegExp(wordToMatch, 'gi');
//console.log(place.street_address.match(regex))
return place.street_address.match(regex)
});
}
displayMatches =() => {
const matchArray = this.findMatches(this.state.searchInput, this.state.links);
const newStateMatches = matchArray.map(place => {
console.log(place.street_address);
return place
});
this.setState({matches:newStateMatches})
this.setState({showMatches:true})
}
alertClicked =(event) => {
//alert('you clicked an item in the group')
const data = event.target
console.log('clicked this data:', data)
this.setState({searchLink: event.target})
console.log(this.state.searchLink)
}
render() {
return (
<div>
<input
placeholder="Search for a Link Near you..."
onChange = {this.handleInputChange}
value = {this.state.searchInput}
/>
<Button onClick={this.handleSubmit}>
Search
</Button>
<ListGroup defaultActiveKey="#link1">
{
this.state.matches.map(match => {
return <Match
address={match.street_address}
alertClicked={this.alertClicked}/>
})
}
</ListGroup>
</div>
);
}
}
export default App;
Match.js
import React from 'react';
import { ListGroup } from 'react-bootstrap';
const match = ( props ) => {
return (
<ListGroup.Item
className="Matches"
action onClick={props.alertClicked}>
<p>{`${props.address}`}</p>
</ListGroup.Item>
)
};
export default match;
I think your initial instinct as to how to do this is correct :
get the matches
store them in state
map over the state and render one component per match, passing the relevant data as props
To answer your question exactly, mapping over state to render component usually looks something like this :
<div>
{
matches.map(match => {
return <Match address={match.address} name={match.name} />
})
}
</div>
You can also destructure properties like this :
<div>
{
matches.map(({address, name}) => {
return <Match address={address} name={name} />
})
}
</div>
Also, another minor observation: you notice I called the component Match with a capital M. It is a convention in React and other component based libraries that components' names are always capitalized, not only in the file name but also in the code.
First move all your method definitions outside of your render function (you'll need to update const and add this.
in your display matches you should be building a newstate array then setState with the new array once built
i do not use react bootstrap but it did not appear that your submit button was within the form therefor was not submitting the form.
Make sure react components are capitalized (match component should be Match)
I passed the whole 'place' down to the Match component via place prop:
<Match place={place} />
if you want to access the address like you did you would need to pass each individual value from the place down to the Match component like:
<Match address={place.address} />
(also if you are only initializing state before first render you can do so outside of the constructor)
I simplified the return statement to just use a plain input and button tag for simplicity but you can probably get going from here
Working Snippet:
const Match = ( props ) => {
return (
<div className="Matches">
<p>{`data is passed: ${props.place.street_address}`}</p>
</div>
)
};
class SomeComponent extends React.Component{
state = {
links: [],
selectedLink:null,
userLocation: {},
searchInput: "",
showMatches: false,
matches: []
}
componentDidMount() {
fetch('https://data.cityofnewyork.us/resource/s4kf-3yrf.json')
.then(res=> res.json())
.then(res=>
//console.log(json)
this.setState({links:res})
);
}
handleInputChange = (event) => {
event.preventDefault()
this.setState({searchInput: event.target.value })
//console.log(event.target.value)
}
handleSubmit = (event) => {
event.preventDefault()
this.displayMatches();
}
findMatches = (wordToMatch, my_obj) => {
return my_obj.filter(place => {
// here we need to figure out the matches
const regex = new RegExp(wordToMatch, 'gi');
//console.log(place.street_address.match(regex))
return place.street_address.match(regex)
});
}
displayMatches =() => {
const matchArray = this.findMatches(this.state.searchInput, this.state.links);
const newStateMatches = matchArray.map(place => {
console.log(place.street_address);
return place
});
this.setState({matches:newStateMatches})
this.setState({showMatches:true})
}
render() {
return (
<div>
<input
placeholder="Search for a Link Near you..."
onChange = {this.handleInputChange}
value = {this.state.searchInput}
/>
<button onClick={this.handleSubmit}>
Search
</button>
{this.state.matches.map((place)=>{
return <Match place={place} />
})}
</div>
);
}
}
ReactDOM.render(
<SomeComponent />,
document.getElementById("react")
);
<div id='react'></div>
<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>

Infinite Scroller duplicating list results [ReactJS]

so I'm working on a pokedex project that is linked to the PokeAPI. I have the main page that loads up the list ans the code for PokemonList.js is as follows:
import React, { Component } from "react";
import PokemonCard from "./PokemonCard";
import axios from "axios";
import InfiniteScroll from "react-infinite-scroller";
export default class PokemonList extends Component {
state = {
url: "https://pokeapi.co/api/v2/pokemon?limit=20&offset=0.",
pokemon: null,
itemsCountPerPage: 20,
activePage: 1,
count: 365,
previous: null
};
loadPokemon = () => {
axios
.get(this.state.url)
.then(res => {
this.setState(prevState => {
return {
pokemon: [...prevState.pokemon, ...res.data.results],
url: res.data.next
};
});
})
.catch(function(error) {
// handle error
console.log(error);
});
};
async componentDidMount() {
const res = await axios.get(this.state.url);
this.setState({ pokemon: res.data["results"] });
}
render() {
console.log(this.state.pokemon);
return (
<React.Fragment>
{this.state.pokemon ? (
<div className="row">
<InfiniteScroll
pageStart={1}
loadMore={this.loadPokemon}
hasMore={true}
loader={
<div className="loader" key={0}>
Loading ...
</div>
}
>
{this.state.pokemon.map((pokemon, i) => (
<PokemonCard
key={pokemon.name + i}
name={pokemon.name}
url={pokemon.url}
/>
))}
</InfiniteScroll>
</div>
) : (
<h1>Loading Pokemon</h1>
)}
</React.Fragment>
/*<React.Fragment>
{this.state.pokemon ? (
<div className="row">
{this.state.pokemon.map(pokemon => (
<PokemonCard
key={pokemon.name}
name={pokemon.name}
url={pokemon.url}
/>
))}
</div>
) : (
<h1>Loading Pokemon</h1>
)}
</React.Fragment>*/
);
}
}
For some reason, the first 20 PokemonCards are loaded in from the API, but once I reach the 20th one, the infinite scroller loads up the first 20 again before eventually loading the next 20-40, then 40-60. It is only the first set amount that is duplicated.
This is a screenshot of the console being returned
As you can see, Bulbasaur is repeated after the 20th data in the list.
You should not called the loadPokemon function inside your componentDidMount(). Remove it and it should work as expected!
import React, { Component } from "react";
import PokemonCard from "./PokemonCard";
import axios from "axios";
import InfiniteScroll from "react-infinite-scroller";
export default class PokemonList extends Component {
state = {
url: "https://pokeapi.co/api/v2/pokemon?limit=20&offset=0.",
pokemon: [],
itemsCountPerPage: 20,
activePage: 1,
count: 365,
previous: null
};
loadPokemon = () => {
axios
.get(this.state.url)
.then(res => {
this.setState(prevState => {
return {
pokemon: [...prevState.pokemon, ...res.data.results],
url: res.data.next
};
});
})
.catch(function(error) {
// handle error
console.log(error);
});
};
render() {
console.log(this.state.pokemon);
return (
<React.Fragment>
{this.state.pokemon ? (
<div className="row">
<InfiniteScroll
pageStart={1}
loadMore={this.loadPokemon}
hasMore={true}
loader={
<div className="loader" key={0}>
Loading ...
</div>
}
>
{this.state.pokemon.map((pokemon, i) => (
<PokemonCard
key={pokemon.name + i}
name={pokemon.name}
url={pokemon.url}
/>
))}
</InfiniteScroll>
</div>
) : (
<h1>Loading Pokemon</h1>
)}
</React.Fragment>
);
}
}
I ended up changing my state from
pokemon: null,
to
pokemon: [],
And I added
const pokemon = prevState.pokemon;
to my loadPokemon Function
In your componentDidMount function, you need to set your this.state.url, just like you do each time loadPokemon is called. You are calling the base url twice for the first page currently, once in componentDidMount and again in loadPokemon.

React is no longer show display from API after hiding API key and API url

import React from "react";
import axios from "axios";
import Suggestions from "../Suggestions"
const API_URL = 'https://newsapi.org/v2/top-headlines?sources=google-news';
const API_KEY = process.env;
console.log('getArticles');
console.log(API_KEY, API_URL);
class Home extends React.Component{
state = {
articles : [],
query: '',
}
getArticles(){
axios.get(`${API_URL}?api_key=${API_KEY}`)
.then((data) => {
this.setState({
articles: data.data
})
})
};
handleInputChange = () => {
this.setState ({
query: this.home.value
},
() => {
if (this.state.query && this.state.query.length >1) {
if (this.state.query.length % 2 === 0) {
this.getArticles()
}
} else if (!this.state.query) {
}
})
}
render(){
return (
<>
<form>
<input
placeholder= "Search for some shit"
ref={input => this.search = input}
onChange={this.handleInputChange}
/>
<Suggestions results={this.state.articles} />
</form>
<div className="ui raised very padded text container segment">
<ul>
{ this.state.articles.slice(0,1).map((article, index) => {
return (<li key={index}>
<h2>Title<br></br>{ article.title }</h2>
<div> Author: { article.author }</div>
<br></br>
<p>{ article.content }</p>
<a href={article.url}>{ article.url }</a>
</li> )
})}
</ul>
</div>
</>
);
}}
export default Home;
My console.log shows my url link along with the key. Maybe im writing getArticles wrong thats why the container not longer shows the API call. Before I hid the key/link it displayed correctly. To be honest im not fully sure what im doing what it comes to hiding keys in react.
Any help would be appreciated

How to create a simple list maker app in React.JS?

I'm working on a simple list maker, to do list app using create-react-app and I'm having some trouble puzzling out the functionality. What I'm trying to accomplish with this app:
I want to be able to enter text into an input, push the button or press enter, and whatever text will be listed on the body of the app.
I want to be able to create a button that will delete the list items once the task or objective is complete
My code is broken up into these components so far:
App,
ListInput,
ItemList,
Item
The code for App is
import React, { Component } from 'react';
import './App.css';
import Navigation from './components/Navigation';
import ListInput from './components/ListInput';
import ListName from './components/ListName';
import Item from './components/Item';
import ItemList from './components/ItemList';
class App extends Component {
constructor() {
super();
this.state = {
input: '',
items: []
};
}
addItem = () => {
this.setState(state => {
let inputValue = this.input.current.value;
if (inputValue !== '') {
this.setState({
items: [this.state.items, inputValue]
})
}
})
}
onButtonEnter = () => {
this.addItem();
}
render() {
return (
<div className="App">
<Navigation />
<ListName />
<ListInput addItem={this.addItem}
onButtonEnter={this.onButtonEnter} />
<Item />
<ItemList />
</div>
);
}
}
export default App;
The code for ListInput is :
import React from 'react';
import './ListInput.css';
const ListInput = ({ addItem, onButtonEnter }) => {
return (
<div>
<p className='center f2'>
{'Enter List Item'}
</p>
<div className='center'>
<div className='center f3 br-6 shadow-5 pa3 '>
<input type='text'
className='f4 pa2 w-70 center'
placeholder='Enter Here'
/>
<button className='w-30 grow f4 link ph3 pv2 dib white bg-black'
onClick={onButtonEnter}
onSubmit={addItem} >
{'Enter'}
</button>
</div>
</div>
</div>
);
}
export default ListInput;
The code for Item is:
import React from 'react';
const Item = ({text}) =>{
return (
<div>
<ul>{text}</ul>
</div>
)}
export default Item;
And the code for ItemList is :
import React from 'react';
import Item from './Item';
const ItemList = ({ items }) => {
return (
<div>
{item.map(items => <Item key={item.id}
text={item.text} />
)}
</div>
)
}
export default ItemList;
In my react app I am returning an error of 'item' is not defined and I'm confused why.
In your App.js you need to pass items as a prop to ItemList component like
<ItemList items={this.state.items} />
Also in addItem function pushing inputValue to items array isn’t correct do something like below
addItem = () => {
this.setState(state => {
let inputValue = this.input.current.value;
if (inputValue !== '') {
this.setState(prevState => ({
items: [...prevState.items, inputValue]
}));
}
})
}
And in ItemList.js do conditional check before doing .map also some typo errors in .map
import React from 'react';
import Item from './Item';
const ItemList = ({ items }) => {
return (
<div>
{items && items.map(item => <Item key={item.id}
text={item.text} />
)}
</div>
)
}
export default ItemList;
Try with above changes This would work
Please excuse me if there are any typo errors because I am answering from my mobile
Your ItemList was not correct. Take a look at corrected snippet below you need to map on items and not item (hence the error item is not defined). Also, you need to items as a prop to ItemList in your app.js
import React from 'react';
import Item from './Item';
const ItemList = ({ items }) => {
return (
<div>
{items.map(item => <Item key={item.id}
text={item.text} />
)}
</div>
)
}
export default ItemList;
In app.js add following line. Also, I don't see what is doing in your app.js remove it.
<ItemList items={this.state.items}/>
Seems like you have a typo in ItemList.
It receives items (plural) as prop but you are using item.
const ItemList = ({ items }) => {
return (
<div>
{items.map(items => <Item key={item.id}
text={item.text} />
)}
</div>
)
}
And don't forget to actually pass the items prop to 'ItemList':
<ItemList items={this.state.items} />

React To-Do app: Removing items, but keeping individual state

I have run into a problem with my React application. So far there is enough functionality in place to add To-Do items to the list, remove them by index and mark them done (text-decoration: line-through).
When I remove an item that is already crossed out, I would expect the other items to keep their own state, however they don't. Here's what I mean.
Let's remove the crossed out item
Why is the bottom one crossed out now?
Here's my code
ToDoApp.js
import React from 'react';
import Header from './Header';
import AddToDo from './AddToDo';
import FilterToDo from './FilterToDo';
import ToDoList from './ToDoList';
import ListButtons from './ListButtons';
export default class ToDoApp extends React.Component {
state = {
toDos: []
};
handleAddToDo = (toDo) => {
if (!toDo) {
return "Nothing was added!";
}
this.setState((prevState) => ({
toDos: prevState.toDos.concat([toDo])
}));
};
handleRemoveToDo = (removeIndex) => {
this.setState((prevState) => ({
toDos: prevState.toDos.filter((toDo, index) => index !== removeIndex)
}));
};
render() {
return (
<div>
<Header />
<AddToDo
handleAddToDo={this.handleAddToDo}
/>
<FilterToDo />
<ToDoList
toDos={this.state.toDos}
handleRemoveToDo={this.handleRemoveToDo}
/>
<ListButtons />
</div>
);
};
};
ToDoList.js
import React from 'react';
import ToDoListItem from './ToDoListItem';
const ToDoList = (props) => (
<div>
<h3>To Do List</h3>
<div>
{props.toDos.map((toDo , index) => (
<ToDoListItem
key={index}
index={index}
toDoTitle={toDo}
handleRemoveToDo={props.handleRemoveToDo}
/>))}
</div>
</div>
);
export default ToDoList;
ToDoListItem.js
import React from 'react';
export default class ToDoListItem extends React.Component {
state = {
done: false
};
handleDoneTrigger = () => {
this.setState((prevState) => ({ done: !prevState.done }));
};
render() {
return (
<div>
<p
className={this.state.done ? "done" : ""}
>{this.props.toDoTitle}</p>
<button onClick={(e) => {
this.props.handleRemoveToDo(this.props.index)
}}>Remove</button>
<button onClick={this.handleDoneTrigger}>Done</button>
</div>
);
}
};
The problem is with this piece of code:
<ToDoListItem
key={index}
index={index}
toDoTitle={toDo}
handleRemoveToDo={props.handleRemoveToDo}
/>))}
as you set the index as key of ToDoListItem. Instead of index assign some unique key to each element because when you delete an item its index assigned to following item in the list.
This will be helpful to dig more into deep: https://medium.com/#robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318

Categories

Resources