ReactJS TypeError: _this3.onDismissID is not a function - javascript

I'm pulling top news related to a particular search term from a website, displaying them and then adding a dismiss button beside each news so that user can delete them if they want. The code goes something like this:
import React, { Component } from "react";
const PATH_BASE = "https://hn.algolia.com/api/v1";
const PATH_SEARCH = "/search";
const PARAM_SEARCH = "query=";
const DEFAULT_QUERY = "redux";
const url = `${PATH_BASE}${PATH_SEARCH}?${PARAM_SEARCH}${DEFAULT_QUERY}`;
console.log(url);
class App extends Component {
constructor(props) {
super(props);
this.state = {
results: null,
searchTerm: DEFAULT_QUERY
};
this.setTopSearchStories = this.setTopSearchStories.bind(this);
this.onDismissID = this.onDismissID.bind(this);
}
setTopSearchStories(results) {
this.setState({ results });
}
onDismissID(id) {
const updatedHits = this.state.results.hits.filter(
(item) => item.objectID !== id
);
this.setState({
result: { ...this.state.results, hits: updatedHits }
});
}
componentDidMount() {
const { searchTerm } = this.state;
fetch(`${PATH_BASE}${PATH_SEARCH}?${PARAM_SEARCH}${searchTerm}`)
.then((response) => response.json())
.then((results) => this.setTopSearchStories(results))
.catch((error) => error);
}
render() {
const { results, searchTerm } = this.state;
if (!results) return null;
const lists = results.hits;
return (
<div className="page">
<Table list={lists} />
</div>
);
}
}
class Table extends Component {
render() {
const { list } = this.props;
return (
<div>
{list.map((item) => (
<div key={item.objectID} className="table">
<span>
<a href={item.url}>{item.title}</a>
</span>
<span>{item.author}</span>
<span>
<button
onClick={() => this.onDismissID(item.objectID)}
type="button"
>
Dismiss
</button>
</span>
</div>
))}
</div>
);
}
}
export default App;
It renders fine, but when I click the Dismiss button the code breaks with the error message "_this3.onDismissID is not a function" . Why is this happening?

Issue
onDismissID isn't defined in Table, it's defined in App.
Solution
Seems you may want to pass onDismissID as a prop to Table.
In App pass an onDismissID prop to the Table component.
...
render() {
const { results, searchTerm } = this.state;
if (!results) return null;
const lists = results.hits;
return (
<div className="page">
<Table list={lists} onDismissID={this.onDismissID} /> // pass this.onDismissID
</div>
);
}
In Table, destructure onDismissID from props and attach to button click handler.
class Table extends Component {
render() {
const { list, onDismissID } = this.props; // destructure onDismissID
return (
<div>
{list.map((item) => (
<div key={item.objectID} className="table">
<span>
<a href={item.url}>{item.title}</a>
</span>
<span>{item.author}</span>
<span>
<button
onClick={() => onDismissID(item.objectID)} // attach onDismissID
type="button"
>
Dismiss
</button>
</span>
</div>
))}
</div>
);
}
}

Related

Understanding async in React components

I'm using axios to return the details of a Pokemon:
class PokeDetails extends React.Component{
constructor(props){
super(props);
this.state = { pokemon: null }
}
componentDidMount(){
axios.get('https://pokeapi.co/api/v2/pokemon/1')
.then(res => {
this.setState({
pokemon: res.data
});
}).catch((err) => { console.log('Axios Poke Details error: ', err) });
}
render(){
const {pokemon} = this.state;
const pokeCard = pokemon ? (
<div className="poke-details">
<img src={pokemon.sprites.front_default} alt={`${pokemon.name} front`}/>
<h3 className="card-title">{pokemon.name}</h3>
</div>
) : (
<div className="center">Loading Pokemon...</div>)
return(
<div className="container">
{pokeCard}
</div>
)
}
}
export default PokeDetails
I want to display the pokemon types, which is an array that has a length of 1 or 2 depending on the pokemon. So I thought:
render(){
const {pokemon} = this.state
const listTypes = pokemon.types.map((type) => { <li>{type.name}</li> });
}
...and render listTypes in a ul in const pokeCard. When I do this I get an error saying pokemon is null. Shouldn't this not happen because of the ternary operator rendering the pokeCard?
Using pokemon before its detail get fetched from service, try like given:
const listTypes = pokemon && pokemon.types.map((type) => { <li>{type.name}</li> });
Problem is that componentDidMount is called after the initial render. So on the initial render, pokemon is null. That is why accessing pokemon.types fails.
You need to access pokemon.types only when pokemon is not null. Just move the code where you .map() over the pokemon.types inside the ternary operator.
const pokeCard = pokemon ? (
<div className="poke-details">
<img src={pokemon.sprites.front_default} alt={`${pokemon.name} front`} />
<h3 className="card-title">{pokemon.name}</h3>
<ul>
{pokemon.types.map(({ type }) => (
<li key={type.name}>{type.name}</li>
))}
</ul>
</div>
) : (
<div className="center">Loading Pokemon...</div>
);
Demo:
class PokeDetails extends React.Component {
constructor(props) {
super(props);
this.state = { pokemon: null };
}
componentDidMount() {
axios
.get("https://pokeapi.co/api/v2/pokemon/1")
.then((res) => {
this.setState({
pokemon: res.data
});
})
.catch((err) => {
console.log("Axios Poke Details error: ", err);
});
}
render() {
const { pokemon } = this.state;
const pokeCard = pokemon ? (
<div className="poke-details">
<img
src={pokemon.sprites.front_default}
alt={`${pokemon.name} front`}
/>
<h3 className="card-title">{pokemon.name}</h3>
<ul>
{pokemon.types.map(({ type }) => (
<li key={type.name}>{type.name}</li>
))}
</ul>
</div>
) : (
<div className="center">Loading Pokemon...</div>
);
return <div className="container">{pokeCard}</div>;
}
}
ReactDOM.render(<PokeDetails />,document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.20.0/axios.min.js"></script>
<div id="root"></div>
Rome data is better kept in cache, in case it gets shared between componenets in different levels.
I reproduced your code:
import React from 'react';
import useSWR from 'swr';
const fetcher = async (...args) => {
const res = await fetch(...args);
return res.json();
};
function PokeDetails() {
const { data, error } = useSWR('https://pokeapi.co/api/v2/pokemon/1', fetcher);
if (error) return <div>failed to load</div>;
if (!data) return <div>loading...</div>;
return (
<div>
{data ? (
data.map((pokemon) => {
return (
<div className='poke-details'>
<img
src={pokemon.sprites.front_default}
alt={`${pokemon.name} front`}
/>
<h3 className='card-title'>{pokemon.name}</h3>
</div>
);
})
) : (
<div>Some loading content</div>
)}
</div>
);
}
export default PokeDetails;
you can check this article to get a clear idea.

React: How to send data on Popup close?

I have this Popup that I built in my React app. It's, in a sense, another page inside the Popup. In this Popup, there is a list. There are no forms at all in this Popup window. I only have another popup inside this popup with a submission form that adds another item to the list.
Therefore, what I'm attempting to do is to submit the list to the Parent component when I click on the "Close" button to close the Popup window. How do I do that?
Here's my code:
SingleBox.js
import React, { Component } from "react";
import SecurityForm from "../SecurityForm/index";
import PriceForm from "../PriceForm/index";
export default class SingleSecuritybox extends Component {
constructor(props) {
super(props);
this.state = {
showPopup: false,
showPricePopup: false, //don't show popup
pricelist: this.props.price
};
}
/* toggle and close popup edit form window */
togglePopup = () => {
this.setState(prevState => ({
showPopup: !prevState.showPopup
}));
};
togglePricePopup = () => {
this.setState(prevState => ({
showPricePopup: !prevState.showPricePopup
}));
};
/* handles edit current security form submissions */
handleEditSecuritySubmission = editSecurity => {
const { editCurrentSecurity, id } = this.props;
this.togglePopup();
editCurrentSecurity({ ...editSecurity, id });
};
updatePrice = updatePrice => {
const { updatePriceList, id } = this.props;
this.togglePricePopup();
updatePriceList({...updatePrice, id});
console.log("editing price", updatePrice);
};
/* handles delete current security form submissions */
handleDeleteSecurity = () => {
const { deleteSecurity, id } = this.props;
// toggle the pop up (close)
this.togglePopup();
// sends the id back to App's "this.deleteSecurity"
deleteSecurity(id);
};
render() {
return (
<div className="box">
<article className="securitytable">
<div className="title">
<h2>
<strong>{this.props.name}</strong>
</h2>
<hr className="lightgray-line" />
</div>
<table>
<tbody>
<tr>
<td className="isin-width">{this.props.isin}</td>
<td className="country-width">{this.props.country}</td>
<td>
<button type="button" className="price-btn" onClick={this.togglePricePopup}>Prices</button>
{this.state.showPricePopup ? (
<PriceForm
pricelist= {this.props.price}
updatePrice={ this.updatePrice }
addPrice={this.props.addPrice}
closePopup= {this.togglePricePopup}
/>
) : null}
</td>
<td className="editing-btn">
<button
type="button"
className="edit-btn"
onClick={this.togglePopup}
>
Edit
</button>
{this.state.showPopup ? (
<SecurityForm
{...this.props}
handleEditSecuritySubmission={ this.handleEditSecuritySubmission }
handleDeleteSecurity={this.handleDeleteSecurity}
cancelPopup={this.togglePopup}
/>
) : null}
</td>
</tr>
</tbody>
</table>
</article>
</div>
);
}
}
This code in question is this list that'll open in Popup window which is a child componenet:
<button type="button" className="price-btn" onClick={this.togglePricePopup}>Prices</button>
{this.state.showPricePopup ? (
<PriceForm
pricelist= {this.props.price}
updatePrice={ this.updatePrice }
addPrice={this.props.addPrice}
closePopup= {this.togglePricePopup}
/>
) : null}
In this child component, which is Price Popup:
import React, { Component } from "react";
import PriceBox from "../SinglePricebox/index";
import AddPriceForm from "../AddPriceForm/index";
export default class PriceForm extends Component {
constructor(props) {
super(props);
this.state = {
priceArr: this.props.pricelist,
showPricePopup: false,
addPricePopup: false,
isToggleOn: true,
date: props.date || "",
number: props.number || ""
};
}
updateInput = ({ target: { name, value } }) =>
this.setState({ [name]: value });
togglePopup = () => {
this.setState(prevState => ({
showPopup: !prevState.showPopup
}));
};
togglePricePopup = () => {
this.setState(prevState => ({
showPricePopup: !prevState.showPricePopup
}));
};
addPricePopup = () => {
this.setState(prevState => ({
addPricePopup: !prevState.addPricePopup
}));
};
/* adds a new price to the list */
addPrice = newPrice => {
this.setState(prevState => ({
addPricePopup: !prevState.addPricePopup,
// spreads out the previous list and adds the new price with a unique id
priceArr: [...prevState.priceArr, { ...newPrice }]
}));
};
handleListSubmit = () => {
const { priceArr } = this.state;
const { updatePrice } = this.props;
const fields = {priceArr};
this.setState(() => {
// if (addPrice) addPrice(fields);
updatePrice(fields);
});
console.log("submission", fields);
};
render() {
return (
<div className="popup">
<div className="popup-inner">
<div className="price-form">
<h2>Prices</h2>
<div className="scroll-box">
{this.state.priceArr.map((props) => (
<PriceBox
{...props}
key={props.date}
/>
))}
</div>
<div className="buttons-box flex-content-between">
<button
type="button"
onClick={this.addPricePopup}
className="btn add-button">Add +</button>
{this.state.addPricePopup && (
<AddPriceForm
addPrice={this.addPrice}
cancelPopup={this.addPricePopup}
/>
)}
<div className="add-btns">
<button
type="button"
onClick={this.handleListSubmit}
className="btn cancel-button"
>
Close
</button>
</div>
</div>
</div>
</div>
</div>
);
}
}
What I'm attempting to do is to send the data (the list array) back to the Parent component on close, but I notice that I can send the data back but I couldn't close the window...
<button
type="button"
onClick={this.handleListSubmit}
className="btn cancel-button"
>
Close
</button>
How do I do this? I cannot add something like this.props.closePopup(); in the handleListSubmit function because while it can close the window, it prevents the list array from being submitted and passed to the Parent component.
You can use parent callback function to send data from child to parent.
on child
handleListSubmit = () => {
...
this.props.onSummited(data)
}

How can I handle deleting an item from list through a modal rendered in the layout(top level)?

I am rendering my modal in my layout and the modal's functionality is to delete a list item in my todo list. How can I pass that handle delete function to the modal?
class TODO extends Component {
handleDelete = (id) => {
const newArr = this.state.TODOList.filter((item, idx) => {id !==
idx})
this.setState({ TODOList: newArr });
}
render () {
return this.state.TODOList.map((item, id) =>
<div>
<ITEMS
idx={id}
id={id}
/>
</div>
)
}
}
class myModal extends Component {
render () {
return (
<div>
<button onClick={...???....}> Delete </Button>
</div>
)
}
}
class Layout extends Component {
render () {
return (
<div>
<myModal />
</div>
)
}
}
You can pass the function down as props
class TodoList extends Component {
constructor () {
this.setState({ TODOList: [...] })
}
handleDelete = (id) => {
const newArr = this.state.TODOList.filter((item, idx) => {id !==
idx})
this.setState({ TODOList: newArr });
}
render() {
return (
<div className="container">
{ return this.TODOList.map(todo, idx => { <TODO idx={idx} /> }) }
<Modal handleDelete={this.handleDelete} />
</div>
)
}
}
class myModal extends Component {
constructor(props) {
super(props)
}
render () {
return (
<div>
<button onClick={() => this.handleDelete(this.idx)}> Delete </Button>
</div>
)
}
}
class Layout extends Component {
render () {
return (
<div>
<myModal handleDelete={this.handleDelete} />
</div>
)
}
}
First you should add <TODO> as child of <Layout>. You can set its ref='todo'.Create a handleDelete in <Layout>. Call the handleDelete of todo using its ref. And pass handleDelete in props of <myModal>. This is for the case when you want to have a state for TODO
class TODO extends Component {
handleDelete = (id) => {
const newArr = this.state.TODOList.filter((item, idx) => {id !==idx})
this.setState({ TODOList: newArr });
}
}
class myModal extends Component {
state = {idToDelete : 2}
render () {
return (
<div>
<button onClick={() => this.props.handleDelete(this.state.idToDelete)}> Delete </Button>
</div>
)
}
}
class Layout extends Component {
render () {
return (
<div>
<myModal handleDelete={handleDelete} />
<TODO ref="todo" />
</div>
)
}
handleDelete = (id) =>{
this.refs.handleDelete(id);
}
}

get props from three imbricate component

I find to get props from three level nested. I have on global component who is my view, inside this I have MovieRow component who return conditional compoenent IsFav or IsNotFav. But in MovieRow component I can't get my prop data.
MovieRow.js :
import React from "react";
import "../css/MovieRow.css";
import { APIKEY, baseURL } from "../../App";
import { filter } from "../../View/Popular";
var myFavoriteMovies = [];
function IsFav(props) {
return (
<div key={props.movie.id} className="MovieRow">
<div>
<img alt="poster" src={props.posterSrc} />
</div>
<div>
<h3>{props.movie.title}</h3>
<p>{props.movie.overview}</p>
<input type="button" onClick={this.viewMovie.bind(this)} value="View" />
<button onClick={props.onClick} className="heart" />
</div>
</div>
);
}
function IsNotFav(props) {
return (
<div key={props.movie.id} className="MovieRow">
<div>
<img alt="poster" src={props.posterSrc} />
</div>
<div>
<h3>{props.movie.title}</h3>
<p>{props.movie.overview}</p>
<input type="button" onClick={this.viewMovie.bind(this)} value="View" />
<button onClick={props.onClick} className="toggled heart" />
</div>
</div>
);
}
class MovieRow extends React.Component {
constructor(props) {
super(props);
this.addFavorite = this.addFavorite.bind(this);
this.deleteFavorite = this.deleteFavorite.bind(this);
this.state = {
isFaved: false
};
}
viewMovie() {
const url = "https://www.themoviedb.org/movie/" + this.props.movie.id;
window.location.href = url;
console.log(this.props.movie.id);
}
addFavorite() {
this.setState({ isFaved: true });
const favMovie = "".concat(
baseURL,
"movie/",
this.props.movie.id,
"?api_key=",
APIKEY
);
myFavoriteMovies.push(favMovie);
}
deleteFavorite() {
this.setState({ isFaved: false });
}
render() {
const isFaved = this.state.isFaved;
let movie;
if (isFaved) {
movie = (
<IsNotFav key={this.props.movie.id} onClick={this.deleteFavorite} />
);
} else {
movie = <IsFav key={this.props.movie.id} onClick={this.addFavorite} />;
}
return <div />;
}
}
export { MovieRow as default, myFavoriteMovies };
View.js
...
componentDidMount() {
let url = "".concat(baseURL, "movie/popular?api_key=", APIKEY);
fetch(url)
.then(res => res.json())
.then(data => {
const results = data.results;
var movieRows = [];
for (let i = results.length - 1; i >= 0; i--) {
results.poster_src =
"https://image.tmdb.org/t/p/w185" + results[i].poster_path;
const movieRow = (
<MovieRow
key={results[i].id}
movie={results[i]}
posterSrc={results.poster_src}
favornot
/>
);
movieRows.push(movieRow);
dataTab.push(results[i]);
}
this.setState({ rows: movieRows });
});
}
...
Edit : I have tried to pass the data from fetch in a global variable for use in MovieRow with forEach but is not clean and my variable array has a strange return. It returns myArray with a length of 0. In the console, the output is [] and under [] I see my object.
You are giving your movie id as key to your IsNotFav and IsFav components. Use the movie prop instead, since key is used by React internally and never really passed to the component.
render() {
const isFaved = this.state.isFaved;
let movie;
if (isFaved) {
movie = <IsNotFav movie={this.props.movie} onClick={this.deleteFavorite} />;
} else {
movie = <IsFav movie={this.props.movie} onClick={this.addFavorite} />;
}
return movie;
}

i cant transfer data from react child to parent ang during click on child set value of input in parent

it is my first React App
i want create simple typeahead(autocomplete)
i want when i click on searched list of item, this item will show in value of my Parent input
now my click doesnt work, working only search by name
it is my parent
`
import React, { Component } from 'react';
import logo from './logo.svg';
import './Search.css';
import Sugg from './Sugg';
class Search extends Component {
constructor(props) {
super(props);
this.onSearch = this.onSearch.bind(this);
this.handleClickedItem = this.handleClickedItem.bind(this);
this.onClick = this.onClick.bind(this);
this.state = {
companies: [],
searchedList: [],
value: ''
}
}
componentDidMount() {
this.fetchApi();
console.log(this.state.companies);
}
fetchApi = ()=> {
const url = 'https://autocomplete.clearbit.com/v1/companies/suggest?query={companyName}';
fetch(url)
.then( (response) => {
let myData = response.json()
return myData;
})
.then((value) => {
let companies = value.map((company, i) => {
this.setState({
companies: [...this.state.companies, company]
})
})
console.log(this.state.companies);
});
}
onSearch(arr){
// this.setState({companies: arr});
};
handleInputChange = () => {
console.log(this.search.value);
let searched = [];
this.state.companies.map((company, i) => {
console.log(company.name);
console.log(company.domain);
const tempName = company.name.toLowerCase();
const tempDomain = company.domain.toLowerCase();
if(tempName.includes(this.search.value.toLowerCase()) || tempDomain.includes(this.search.value.toLowerCase())) {
searched.push(company);
}
})
console.log(searched);
this.setState({
searchedList: searched
})
if(this.search.value == '') {
this.setState({
searchedList: []
})
}
}
handleClickedItem(data) {
console.log(data);
}
onClick = e => {
console.log(e.target.value)
this.setState({ value: e.target.value});
};
render() {
return (
<div className="Search">
<header className="Search-header">
<img src={logo} className="Search-logo" alt="logo" />
<h1 className="Search-title">Welcome to React</h1>
</header>
<form>
<input
placeholder="Search for..."
ref={input => this.search = input}
onChange={this.handleInputChange}
/>
<Sugg searchedList={this.state.searchedList} onClick={this.onClick.bind(this)} />
</form>
</div>
);
}
}
export default Search;
`
and here my child component
i dont know how call correctly click event
import React from 'react';
const Sugg = (props) => {
console.log(props);
const options = props.searchedList.map((company, i) => (
<div key={i} >
<p onClick={() => this.props.onClick(this.props)}>{company.name}</p>
</div>
))
console.log(options);
return <div >{options}</div>
}
export default Sugg;
please help me who knows how it works
thanks a lot
In the parent you could modify your code:
onClick = company => {
console.log('company', company);
this.setState({ value: company.name});
};
and you don't need to bind this because onClick is an arrow function
<Sugg searchedList={this.state.searchedList} onClick={this.onClick} />
and in the child component, you need to use props from the parameters, not from the this context:
<p onClick={() =>props.onClick(company)}>{company.name}</p>

Categories

Resources