I'm new on React. I wrote a project on which there is a search component. the search works fine ( I checked on console.log) but I don't know how to call the stateless function component on which the search results should be shown?
class SearchCard extends Component {
// qQuery is a variable for query input
state = { qQuery: "" };
HandleSearch= async (e) => {
e.preventDefault();
const {data:cards} = await cardService.getAllCards();
var searchResults = cards.filter((item) =>
item.qTopic.includes(this.state.qQuery) ||
item.qArticle.includes(this.state.qQuery)
);
this.setState({ cards : searchResults });
// console.log('search results ',searchResults, ' cards ',this.state);
return <CardRender cards={cards}/>
}
render() {
return (
<React.Fragment>
<form className="form" onSubmit={ this.HandleSearch }>
<div className="input-group md-form form-sm form-1 pl-4 col-12">
const CardRender = ({cards,favs,onHandleFavs}) => {
return (
<div className="row">
{cards.length > 0 &&
cards.map((card) =>
<Card key={card._id}
card={card}
favs={favs}
onHandleFavs={() => onHandleFavs(card._id)}
/>
}
</div>
);
}
export default CardRender;
screenshot
You should add the <CardRender cards={cards}/> to the element render returns (at the place you want it to be) and render it if state.cards is not empty.
Something like this
class SearchCard extends Component {
// qQuery is a variable for query input
state = { qQuery: "" };
HandleSearch= async (e) => {
// ...
this.setState({ cards : searchResults });
}
render() {
return (
<div>
...
{cards?.length && <CardRender cards={cards}/>}
</div>
);
}
}
Related
I am trying to implement an onChange method that when the user type something it gets updated in real time and displayed in the div. The component that I am talking about is at the end of the code and it's called and it is an input that will be rendered 4 times on the dom. For a reason no value get shown on the div I mean {this.state.stake}. Could anyone help me in fixing that? Thanks
import React, { Component } from 'react';
import Stake from './stake';
class FetchRandomBet extends Component {
constructor(props) {
super(props);
this.state = {
loading: true,
bet: null,
value: this.props.value,
stake: ''
};
}
async componentDidMount() {
const url = "http://localhost:4000/";
const response = await fetch(url);
const data = await response.json();
this.setState({
loading: false,
bet: data.bets,
});
}
changeStake = (e) => {
this.setState({
stake: [e.target.value]
})
}
render() {
const { valueProp: value } = this.props;
const { bet, loading } = this.state;
if (loading) {
return <div>loading..</div>;
}
if (!bet) {
return <div>did not get data</div>;
}
return (
< div >
{
loading || !bet ? (
<div>loading..</div>
) : value === 0 ? (
<div className="bet-list">
<ol>
<p>NAME</p>
{
bet.map(post => (
<li key={post.id}>
{post.name}
</li>
))
}
</ol>
<ul>
<p>ODDS</p>
{
bet.map(post => (
<li key={post.id}>
{post.odds[4].oddsDecimal}
<div className="stake-margin">
<Stake
onChange={this.changeStake} />
{this.state.stake}
</div>
</li>
))
}
</ul>
</div>
Pass this.state.stake as a prop of Stake component.
<Stake
onChange={this.changeStake}
stake={this.state.stake}
/>
Then inside of the Stake component assign stake prop to value on an the input. It would look something like this.
const Stake =({stake, onChange})=>{
return <input value={stake} onChange={onChange} />
}
This is my parent Component having state ( value and item ). I am trying to pass value state as a props to child component. The code executed in render method is Performing toggle when i click on button. But when i call the list function inside componentDidMount, Toggle is not working but click event is performed.
import React, { Component } from 'react'
import Card from './Components/Card/Card'
export class App extends Component {
state = {
values : new Array(4).fill(false),
item : [],
}
toggleHandler = (index) => {
console.log("CLICKED");
let stateObject = this.state.values;
stateObject.splice(index,1,!this.state.values[index]);
this.setState({ values: stateObject });
}
list = () => {
const listItem = this.state.values.map((data, index) => {
return <Card key = {index}
show = {this.state.values[index]}
toggleHandler = {() => this.toggleHandler(index)} />
})
this.setState({ item : listItem });
}
componentDidMount(){
// if this is not executed as the JSX is render method is executed everything is working fine. as props are getting update in child component.
this.list();
}
render() {
return (
<div>
{/* {this.state.values.map((data, index) => {
return <Card key = {index}
show = {this.state.values[index]}
toggleHandler = {() => this.toggleHandler(index)} />
})
} */}
{this.state.item}
</div>
)
}
}
export default App
This is my child Component where the state is passed as props
import React from 'react'
const Card = (props) => {
return (
<div>
<section>
<h1>Name : John Doe</h1>
<h3>Age : 20 </h3>
</section>
{props.show ?
<section>Skills : good at nothing</section> : null
}
<button onClick={props.toggleHandler} >Toggle</button>
</div>
)
}
export default Card
I know the componentDidMount is executed only once. but how to make it work except writing the JSX directly inside render method
make a copy of the state instead of mutating it directly. By using [...this.state.values] or this.state.values.slice()
toggleHandler = (index) => {
console.log("CLICKED");
let stateObject = [...this.state.values]
stateObject = stateObject.filter((_, i) => i !== index);
this.setState({ values: stateObject });
}
Also in your render method, this.state.item is an array so you need to loop it
{this.state.item.map(Element => <Element />}
Also directly in your Render method you can just do
{this.state.values.map((data, index) => {
return <Card key = {index}
show = {this.state.values[index]}
toggleHandler = {() => this.toggleHandler(index)} />
})}
In your card component try using
<button onClick={() => props.toggleHandler()}} >Toggle</button>
Value should be mapped inside render() of the class component in order to work
like this:
render() {
const { values } = this.state;
return (
<div>
{values.map((data, index) => {
return (
<Card
key={index}
show={values[index]}
toggleHandler={() => this.toggleHandler(index)}
/>
);
})}
</div>
);
}
check sandbox for demo
https://codesandbox.io/s/stupefied-spence-67p4f?file=/src/App.js
In my website, it currently shows users a list of movies based on their input.
When user clicks on the title of the movie that is rendered, I want to setState(chosenOne: the movie title they clicked).
Currently, when I click on movie title, it returns an error stating the following:
Uncaught TypeError: Cannot read property 'onClick' of undefined
at onClick (Fav.js:62)
Any way to fix this?
Any help is greatly appreciated.
import React, { Component } from 'react'
import axios from 'axios';
import '../styles/Rec.scss'
export class Fav extends Component {
constructor (props) {
super (props)
this.state = {
inputOne: '',
chosenOne: '',
movies:[],
};
}
onChangeOne = (event) => {
this.setState({
inputOne: event.target.value
},()=>{
if(this.state.inputOne && this.state.inputOne.length > 1) {
this.getInfo()
} else {
}
})
}
onClick = (event) =>{
this.setState({
chosenOne: event.currentTarget.textContent
})
console.log(this.state.chosenOne)
}
onSubmit = (event) => {
event.preventDefault();
}
getInfo = () => {
let url = `https://api.themoviedb.org/3/search/movie?api_key=''&language=en-US&query='${this.state.inputOne}'&page=1&include_adult=false`
axios.get(url)
.then(res => {
if (res.data) {
const movieData = res.data.results.filter(movie => movie.poster_path != null);
this.setState({movies: movieData});
}
console.log(this.state.movies)
})
}
render() {
return (
<div>
<h1>Favorite Movie of All Time</h1>
<form onSubmit={this.onSubmit}>
<input onChange={this.onChangeOne}/>
<div className="rec__container">
{this.state.movies && this.state.movies.slice(0,3).map(function(movie, genre_ids) {
return(
<div className="rec__sample">
<img className="rec__img" src={`https://image.tmdb.org/t/p/w500/${movie.poster_path}`} alt="movie poster"/>
<p onClick={event => this.onClick(event)}>{movie.title}</p>
</div>
)
})}
</div>
</form>
</div>
)
}
}
export default Fav
I am quite sure the problem is that this in this.onClick is undefined. This happens when it is not bound correctly to the the class.
I would recommend to change the function declared after map to an arrow function.
<div className="rec__container">
{this.state.movies &&
this.state.movies.slice(0, 3).map((movie, genre_ids) => {
return (
<div className="rec__sample">
<img className="rec__img" src={`https://image.tmdb.org/t/p/w500/${movie.poster_path}`} alt="movie poster" />
<p onClick={event => this.onClick(event)}>{movie.title}</p>
</div>
);
})}
</div>;
Also, you are binding onClick function in your constructor as well as using it as an arrow function. Bind does not work on arrow functions.
Either remove this.onClick = this.onClick.bind(this) or convert onClick into a simple function rather than an arrow function.
I'm fairly new to react.
My search input and pagination buttons aren't triggering anything and nothing comes up in the console, what is wrong with my code ?
I tried putting every functions in App.js to get it cleaner.
App.js
import React, { Component } from "react";
import List from './List';
let API = 'https://swapi.co/api/people/';
class App extends Component {
constructor(props) {
super(props);
this.state = {
results: [],
search: '',
currentPage: 1,
todosPerPage: 3
};
this.handleClick = this.handleClick.bind(this);
this.updateSearch = this.updateSearch.bind(this);
}
componentWillMount() {
this.fetchData();
}
fetchData = async () => {
const response = await fetch(API);
const json = await response.json();
this.setState({ results: json.results });
};
handleClick(event) {
this.setState({
currentPage: Number(event.target.id)
});
}
updateSearch(event) {
this.setState({
search: event.target.value.substr(0, 20)
});
}
render() {
return (
<div>
<List data={this.state} />
</div>
);
}
}
export default App;
List.js
import React, { Component } from 'react';
import Person from './Person';
class List extends Component {
render() {
const { data } = this.props;
const { results, search, updateSearch, handleClick, currentPage, todosPerPage } = data;
const indexOfLastTodo = currentPage * todosPerPage;
const indexOfFirstTodo = indexOfLastTodo - todosPerPage;
const currentTodos = results.slice(indexOfFirstTodo, indexOfLastTodo).filter(item => {
return item.name.toLowerCase().indexOf(search) !== -1;
});
const renderTodos = currentTodos.map((item, number) => {
return (
<Person item={item} key={number} />
);
});
const pageNumbers = [];
for (let i = 1; i <= Math.ceil(results.length / todosPerPage); i++) {
pageNumbers.push(i);
}
const renderPageNumbers = pageNumbers.map(number => {
return (
<li className="page-link" key={number} id={number} onClick={handleClick} style={{cursor: "pointer"}}>{number}</li>
);
});
return (
<div className="flex-grow-1">
<h1>Personnages de Star Wars</h1>
<form className="mb-4">
<div className="form-group">
<label>Rechercher</label>
<input
className="form-control"
type="text"
placeholder="luke skywalker..."
value={search}
onChange={updateSearch}
/>
</div>
</form>
<div className="row mb-5">{renderTodos}</div>
<nav aria-label="Navigation">
<ul id="page-number" className="pagination justify-content-center">{renderPageNumbers}</ul>
</nav>
</div>
);
}
}
export default List;
The value of the input doesn't change one bit if I type in it and if I right click on a page number, the console gets me Uncaught DOMException: Failed to execute 'querySelectorAll' on 'Element': '#4' is not a valid selector.
Any idea ?
The issue is that in the List class you attempt take updateSearch and handleClick out of data (which in turn comes from this.props). But updateSearch and handleClick are never placed inside data. If you log either of these methods to the console you'll see they are undefined.
To fix this, you need to pass updateSearch and handleClick from App to List. You can do this either by including the methods inside the data prop, or by passing them directly as their own props (which I would recommend).
For example, you can change the render method of App to look something like this:
render() {
return (
<div>
<List
data={this.state}
updateSearch={ this.updateSearch }
handleClick={ this.handleClick }
/>
</div>
);
}
Then in the render method of List you can do this:
const { data, updateSearch, handleClick } = this.props;
and remove the definitions of the two methods from the destructuring of data below.
i am struggling to do the functional testing using jest and enzyme.
when i write the test cases it always returns NULL.
The function i am mocking always returns NULL.
i have tried so may examples for spying or mocking the functions but still i will get errors.
basically i need to call this onClickButtonNext in Jest.
class ProfileType extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedProfileType: ''
}
}
componentDidMount() {
this.setState({ selectedProfileType: this.props.profileType });
this.props.setWorkflowDirty();
}
onClickButtonNext = () => {
this.props.onClickNext({ profileType: this.state.selectedProfileType });
}
onClickButtonCancel = () => {
this.props.onClickCancel();
}
render() {
const menus = ["Contact"];
const profileOptions = ProfileTypes.map((profile, i) => {
profile.selected = this.state.selectedProfileType === profile.name;
return (
<IconCard key={i} profile={profile} index={i} type="radio" onSelectProfile={() => this.setState({ selectedProfileType: profile.name })} />
)
}
);
return (
<ScreenCover isLoading={this.props.isLoading}>
<CoreoWizScreen menus={menus} activeFlowId={0} isNextDisabled={this.state.selectedProfileType === '' || this.state.selectedProfileType === 'Guardian'} onNextClick={this.onClickButtonNext} onCancelClick={this.onClickButtonCancel}>
<div className="container-fluid mainContent px-5 d-flex align-items-start flex-column">
<div className="row d-block">
<div className="col-md-12 py-5 px-0">
<h4 className="font-weight-normal mb-4">Select My Profile Type</h4>
<PanelCard>
{profileOptions}
</PanelCard>
</div>
</div>
</div>
</CoreoWizScreen>
<CoreoWizFlow coreoWizNavigationData={CoreoWizNavigationData} activeFlowId={0} />
</ScreenCover>
)
}
}
function mapDispatchToProps(dispatch) {
return {
onClickCancel: () => dispatch(onCancelClick()),
onClickNext: (data) => dispatch(onProfileTypeNext(data)),
setWorkflowDirty: () => dispatch(setWorkflowDirty())
}
}
function mapStateToProps(state) {
return {
profileType: state.onboardingState.profileData.profileType,
isLoading: state.onboardingState.loading
}
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(ProfileType));
test.js
it('ProfileType', () => {
let wrapper = shallow(<ProfileType/>);
wrapper.instance().onClickButtonNext = jest.fn();
wrapper.update();
wrapper.instance().onClickButtonNext;
expect(wrapper.instance().onClickButtonNext).toHaveBeenCalled;
}
this is not working at all. Please help me!!!