So I started using reactjs and I've managed to loop through some XML data but having issues adding a search/filter into it.
This is my code so far:
import React, { Component } from 'react';
import XMLMapping from 'xml-mapping';
import axios from 'axios';
class Guests extends Component {
constructor(props) {
super(props);
this.state = {
guests: [],
search: 'Search Guests'
};
}
componentDidMount() {
axios.get('http://localhost:8080/guestlist.xml')
.then(res => {
const xml = XMLMapping.load(res.data);
var guests = XMLMapping.tojson(xml);
this.setState({guests: guests});
//console.log(guests);
return guests;
});
}
updateSearch(event) {
this.setState({
// Limit to 10 characters only for search
search: event.target.value.substr(0, 10)
});
// console.log(this.state.search); // this will show the previous value of state.
}
render() {
function mapObject(object, callback) {
return Object.keys(object).map(function (key) {
return callback(key, object[key]);
});
}
const firstname = mapObject(this.state.guests, function (key, value) {
return <div key={key}>
{value.record
.map((item,index) => {
//console.log(index)
return <div className="columns" key={index}>
<div className="column" key={index}>{item.first_name.$t} {item.last_name.$t}</div>
<div className="column" >{item.company.$t}</div>
</div>;
})}
</div>
});
let filteredGuests = mapObject(this.state.guests, function (key, value) {
value.record.filter(
(contact) => {
return contact.first_name.$t.indexOf(this.state.search) !== -1;
//console.log(this.state.search)
}
);
});
return (
<div>
<div className="container">
<section className="section">
<h1 className="title">Guests attending Event A</h1> <input className="text" type="text" value={this.state.search} onChange={this.updateSearch.bind(this)} />
<div className="columns"><div className="column">Name</div><div className="column">Company</div></div>
{firstname}
</section>
</div>
</div>
);
}
}
export default Guests;
But it seems to be throwing an error TypeError: Cannot read property 'state' of undefined
This is the line return contact.first_name.$t.indexOf(this.state.search) !== -1;
Any advice or feedback would be appreciate!
As Prakash sharma wrote, you used the wrong function context.
Try to replace function with arrow function:
let filteredGuests = mapObject(this.state.guests, (key, value) => {
value.record.filter(
(contact) => {
return contact.first_name.$t.indexOf(this.state.search) !== -1;
//console.log(this.state.search)
}
);
})
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} />
}
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>
);
}
}
I'm building a mini app and I want to get it cleaner.
So basically I want to have 3 components : App, List and Person.
Here is the code :
App.js
import React, { Component } from "react";
import List from './List';
class App extends Component {
constructor(props) {
super(props);
this.state = {
results: [],
search: '',
currentPage: 1,
todosPerPage: 3
};
this.handleClick = this.handleClick.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 (
<List />
);
}
}
export default App;
List.js
import React, { Component } from 'react';
import Person from './Person';
class List extends Component {
render() {
const { results, currentPage, todosPerPage } = this.state;
const indexOfLastTodo = currentPage * todosPerPage;
const indexOfFirstTodo = indexOfLastTodo - todosPerPage;
const currentTodos = results.slice(indexOfFirstTodo, indexOfLastTodo).filter(item => {
return item.name.toLowerCase().indexOf(this.state.search) !== -1;
});
const renderTodos = currentTodos.map((item, index) => {
return (
<Person item={this.state.item} index={this.state.index}/>
);
});
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={this.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={this.state.search}
onChange={this.updateSearch.bind(this)}
/>
</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;
Person.js
import React, { Component } from 'react';
function Person(item, index) {
return (
<div className="col-lg-4 mb-4" key={index}>
<div className="card">
<div className="card-header">
<h4 className="mb-0">{item.name}</h4>
</div>
<div className="card-body">
<h5 className="card-title">Caractéristiques</h5>
<ul>
<li>Année de naissance : {item.birth_year}</li>
<li>Taille : {item.height} cm</li>
<li>Masse : {item.mass}</li>
<li>Couleur des yeux : {item.eye_color}</li>
<li>Couleur de cheveux : {item.hair_color}</li>
<li>Couleur de peau : {item.skin_color}</li>
</ul>
Sa fiche
</div>
</div>
</div>
)
}
export default Person;
My issue is that I get TypeError: Cannot read property 'results' of null when rendering.
Is it possible to have variable go into every file if I define them all in App.js ?
You are not passing the data the correct way. Try this:
In App.js pass to List component the needed data:
render() {
return (
<List data={this.state}/>
);
}
Then in render() method in List.js get the passed data prop, then extract the data from there:
render() {
const { data } = this.props;
const { results, search, currentPage, todosPerPage } = data;
// ...
// in currentTodos function dont use this.state.search but just "search", that we got above from the data variable
// ...
// also your renderTodos should look like this - use the item and index variables
const renderTodos = currentTodos.map((item, index) => {
return (
<Person item={item} index={index}/>
);
});
// ...
}
So your List.js should look like this:
import React, { Component } from 'react';
import Person from './Person';
class List extends Component {
render() {
// get the data
const { data } = this.props;
// get the properties
const { results, search, currentPage, todosPerPage } = data;
const indexOfLastTodo = currentPage * todosPerPage;
const indexOfFirstTodo = indexOfLastTodo - todosPerPage;
const currentTodos = results.slice(indexOfFirstTodo, indexOfLastTodo).filter(item => {
// use "search" variable
return item.name.toLowerCase().indexOf(search) !== -1;
});
const renderTodos = currentTodos.map((item, index) => {
return (
// use item and index
<Person item={item} index={index}/>
);
});
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={this.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} // use search variable here too
onChange={this.updateSearch.bind(this)}
/>
</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;
And your function in Person.js should have the following declaration, because the parameters are extracted from the passed props:
function Person({item, index}) {
// ...
}
You can use pass variables in your props of <List /> component by passing state inside render function of App.js while calling <List /> like this
render() {
//Passing Data inside props
<List data={this.state} />
}
and inside your List.js, You can access the data variable
const { results, currentPage, todosPerPage } = this.props.data;
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!!!
My first thought is to do it the conventional way, i.e. gather all data as strings in a function and return it as dangerouslySetHtml. I could later call the function as {this.renderList()} in render(). How do I do it the "React Way", i.e. build JSX elements in a method and implement it in render()? Following is a random example of how I'd approach this situation.
constructor(props)
{
super(props);
this.state = {
users: [{..}, {..}, {..}],
info: [{..}, {..}]
};
}
renderSelectDropdown()
{
data = '<select>'
this.state.users.forEach(user =>
{
data += '<optgroup label={user.title}>';
for (let item in this.state.info)
{
this.state.info[user.name][item].forEach(s => {
data += '<option>'+ s +'</option>';
})
}
data += '</optgroup>'
});
data += '</select>';
return {__html: data};
}
render()
{
return (
<div>
<input type="text" name="name">
<div dangerouslySetInnerHTML = this.renderSelectDropdown()></div>
</div>
)
}
Try this:
renderSelectDropdown() {
let list = [];
this.state.users.forEach((user, idx) =>
{
let opt = this.state.info.map((item) => {
this.state.info[user.name][item].map((s, key) => {
<option key={key}>{s}</option>;
})
});
list.push(<optgroup key={idx} label={user.title}>{opt}</optgroup>);
});
return list;
}
render() {
let list = renderSelectDropdown();
return (
<div>
<input type="text" name="name">
<select>
{list}
</select>
</div>
)
}
The react way is this simple
<div>
{this.state.users.map(user => {
return <p> {user.prop} </p>
})}
</div>