How can we access object which is not defined? - javascript

I am trying to create a simple Todo List in ReactJS, while the code is working, I'm having trouble understanding a few pieces of code.
My Application consists of 3 components namely:
index.js - The entry point for the application.
TodoList.js - Rendering the forms, button and TodoItem.js component
TodoItem.js - Actual list which maps over the input text and displays the list.
My TodoList.js component:
import React, { Component } from "react";
import TodoItems from "./TodoItems";
class TodoList extends Component {
constructor(props) {
super(props);
this.state = {
items: []
};
this.addItem = this.addItem.bind(this);
}
addItem(e) {
if (this._inputElement.value !== "") {
var newItem = {
text: this._inputElement.value,
key: Date.now()
};
this.setState(prevState => {
return {
items: prevState.items.concat(newItem)
};
});
}
//console.log(this._inputElement.value);
this._inputElement.value = "";
e.preventDefault();
}
render() {
return (
<div className="todoListMain">
<div className="header">
<form onSubmit={this.addItem}>
<input
ref={a => (this._inputElement = a)}
placeholder="enter task"
></input>
<button type="submit">add</button>
</form>
</div>
<TodoItems entries={this.state.items} />
</div>
);
}
}
export default TodoList;
The TodoItems.js component:
import React, { Component } from "react";
class TodoItems extends Component {
createTasks(itemmm) {
return <li key={itemmm.key}>{itemmm.text}</li>;
}
render() {
var todoEntries = this.props.entries;
var listItems = this.props.entries.map(this.createTasks);
// console.log(this.props.entries);
return <ul className="theList">{listItems}</ul>;
}
}
export default TodoItems;
What is the problem?
1) I'm having a hard time understanding how return <li key={itemmm.key}>{itemmm.text}</li>;
works, as "itemmm" is a random parameter which I have passed, also how "itemmm.text" correctly displays the text, as per my understanding "itemmm" is a vague object and I have not defined it anywhere.
2) Importance of <TodoItems entries={this.state.items} /> line of code in my "TodoList.js" component. so far I understand it is using props to dynamically enter a value. But how is it tinkering with the TodoItem.js component?
Thank you for reading,sorry if its a too basic question. Any help would be much appreaciated.

1.understanding "itemmm" is a vague object
createTasks(itemmm) {
return <li key={itemmm.key}>{itemmm.text}</li>;
}
Edit: regarding key and text
var newItem = {
text: this._inputElement.value,
key: Date.now()
};
this.setState(prevState => {
return {
items: prevState.items.concat(newItem)
};
});
The logic starts from here.
You get items as [{key:23213123, text:'xyz'}]
var listItems = this.props.entries.map(this.createTasks);
Here itemmm is the parameter and you can define it with any Name.
Items are passed into TodoItems to entries as props.

Abhinav you are passing items as props in entries and when you do map on an array there is a callback function which is createTask.
createTasks(itemmm) {
return <li key={itemmm.key}>{itemmm.text}</li>;
}
the above function is callback of map method of code below
this.props.entries.map(createTasks);
It is the same thing which i specify below.
this.props.entries.map((itemmm)=>{
return <li key={itemmm.key}>{itemmm.text}</li>;
});

Related

Mapping an Array of state in Preact

Not used Preact before so I'm wondering whether this is just another quirk of the library. Though I'm trying to map an array of state as you normally would but it's just not outputting the <li> like I thought.
import { h, Preact, setState } from 'preact';
import { useEffect } from 'preact/hooks';
import style from './style.css';
const state = { todos: [], text: '' };
const submitTodo = (e) => {
e.preventDefault(e);
addItem();
}
const addItem = () => {
state.todos.push(inputValue());
}
const inputValue = () => {
const todoInput = document.querySelector('.todoInput');
let inputItem = todoInput.value;
return inputItem;
}
const ToDo = () => (
<div class={style.todo}>
<h1 class="title">ToDo</h1>
<ul class="todoList">
{state.todos.map((item, i) =>{
<li key={i}>{item}</li>
})}
</ul>
<form onSubmit={addItem}>
<input type="text" placeholder="Add a todo item here..." class="todoInput" onInput={inputValue}/>
<button type="submit" class="updateBtn" onClick={submitTodo}>Add Task</button>
</form>
</div>
);
export default ToDo;
I've checked that the code I wrote actually maps the state correctly (which it does via console log anywhere else) but inside the ToDo component it just won't map.
Hopefully someone can tell me what rookie error I'm making? I'd be very grateful if you could help me map out the state correctly in the <li>!
You need to make ToDo rerender after state change. Otherwise it will always show the initial state.

Displaying Multiple API Responses in React

I am learning React and I have a solution that requests information through an API, and when there is a response it sets the state, however when rendering my results it only shows the last response on screen,
Even though there are 4, see image below.
App.js
import React from 'react';
import Tiles from './components/Tiles'
import Form from './components/Form'
import WaterData from './components/WaterData'
class App extends React.Component{
state = {
station_name: undefined,
water_value: undefined,
dateTime: undefined
}
getData = async (e) => {
e.preventDefault();
const name = e.target.elements.name.value;
const api_call = await fetch(`https://waterlevel.ie/geojson/latest/`)
.then(response1 => {
response1.json().then(data =>{
Array.from(data.features).forEach(element => {
if(element.properties['station.name'] === name){
this.setState({
station_name: element.properties['station.name'],
water_value: element.properties['value'],
dateTime: element.properties['datetime'],
});
}
})
});
});
}
render(){
return(
<div>
<Tiles />
<Form loadData={this.getData}/>
<WaterData
station_name={this.state.station_name}
water_value={this.state.water_value}
dateTime={this.state.dateTime}
/>
</div>
)
}
}
export default App;
WaterData.js
import React from 'react';
const Weather = (props) => {
console.log(props)
return(
<li>
<p>Location {props.station_name}</p>
<p>Value {props.water_value}</p>
<p>Date Time: {props.dateTime}</p>
</li>
)
}
export default Weather;
Can someone explain to me why the 4 responses do not display?
This happens because you are replacing the values in your state for each part of your data.
You can filter out the element you want in your array using filter.
And then put the whole array into your state only once :
const api_call = await fetch(`https://waterlevel.ie/geojson/latest/`)
.then(response1 => {
response1.json().then(data => {
const features = Array.from(data.features)
.filter(el => el.properties['station.name'] === name);
this.setState({ features });
})
});
But now, to render all of them, you will need to map your state values :
render(){
return(
<div>
<Tiles />
<Form loadData={this.getData}/>
{this.state.features.map(feat => <WaterData
key={/* Find something unique*/}
station_name={feat.properties['station.name']}
water_value={feat.properties['value']}
dateTime={feat.properties['datetime']}
/>)}
</div>
)
}
There's no need to store all the value separately in your state if they are related to each other, it would be fine for your child component though.
To be sure that the state value is always an array, give it an empty array at the start of your class :
state = {
features: []
}

Can't call class functions from main, "is not a function" error

I'm attempting to learn react by creating a simple weather app which uses api requests to get the data. The api requires a location key which can be obtained through another function. this means i have to return and then pass the key along to the next function but I seem to be having some trouble doing this!
TypeError: _LocationKey__WEBPACK_IMPORTED_MODULE_3__.default.setLocation is not a function" is the error i am given when I attempt to run.
I'm assuming I made an error with how i am calling the functions as well as how the actual classes are structured. Any help would be appreciated!
//The main app
function App() {
//runs function to set api location key
new LocationKey();
LocationKey.setLocation('winnipeg');
//uses get location key to set weatherData
new Weather();
Weather.getWeatherData(LocationKey.getLocation());
//displays location and weather information
return (
<div className="body">
<LocationKey/>
<Weather/>
</div>
);
}
//The location key class
export class LocationKey extends Component{
state = {
locations: []
}
setLocation(name) {
axios.get('http://dataservice.accuweather.com/locations/v1/cities/search?apikey=oqAor7Al7Fkcj7AudulUkk5WGoySmEu7&q=' + name + '&language=en&details=false')
.then(res => {
const locations = res.data;
this.setState({ locations });
})
}
getLocation(){
return this.state.locations[0].Key;
}
render() {
return (
<ul>
{ this.state.locations.slice(0, 1).map(location => <li>{location.EnglishName}</li>)}
</ul>
)
}
}
export default LocationKey
//Weather Class
export class Weather extends Component{
state = {
weatherData: []
}
//issues command to find weather data from api using location key
getWeatherData(location) {
axios.get('http://dataservice.accuweather.com/forecasts/v1/daily/1day/' + location + '?apikey=oqAor7Al7Fkcj7AudulUkk5WGoySmEu7&language=en&details=false&metric=true%20HTTP/1.1')
.then(res => {
const weatherData = res.data;
this.setState({ weatherData });
})
}
render() {
return (
<ul>
{ this.state.weatherData.slice(0, 2).map(weather => <li>{weather.Headline.Text}</li>)}
{ this.state.weatherData.slice(0, 2).map(weather => <li>{weather.Headline.Category}</li>)}
{ this.state.weatherData.slice(0, 2).map(weather => <li>{weather.DailyForecasts.Maximum.Value}</li>)}
{ this.state.weatherData.slice(0, 2).map(weather => <li>{weather.DailyForecasts.Minimum.Value}</li>)}
</ul>
)
}
}
export default Weather
The problem
The issue lies with this line:
LocationKey.setLocation('winnipeg');
You instantiate a new instance of LocationKey, but seeing as LocationKey isn't a singleton and setLocation is not a static method, you're trying to call an instance method in a static way.
Another potential issues is this line:
Weather.getWeatherData(LocationKey.getLocation());
As I said above, you instantiate a new instance of Weather, but since it isn't doing anything and isn't assigned to anything, it gets immediately thrown away, so the following line has no effect on it.
The solution
I would suggest making App its own component and having it contain the state for the locations, then passing the locations array to each of the components.
class App extends React.Component {
state = {
locations: ['winnipeg'],
locationInput: ''
};
render() {
return (
<div>
<input value={this.state.locationInput} onChange={e => this.setState({ locationInput: e.target.value })} />
<button onClick={this.handleAddLocation}>add to locations</button>
<Weather locations={this.state.locations} />
<LocationKey locations={this.state.locations} />
</div>
)
}
handleAddLocation = () => {
if (this.state.locationInput === '') return;
this.setState({
locationInput: '',
locations: [...this.state.locations, this.state.locationInput]
});
};
}
class Weather extends React.Component {
static defaultProps = {
locations: []
}
render () {
return (
<div>
<h2>Weather Component</h2>
<h3>Locations provided:</h3>
<span>{this.props.locations.join(', ')}</span>
</div>
);
}
}
class LocationKey extends React.Component {
static defaultProps = {
locations: []
}
render () {
return (
<div>
<h2>LocationKey Component</h2>
<h3>Locations provided:</h3>
<span>{this.props.locations.join(', ')}</span>
</div>
);
}
}
ReactDOM.render( < App / > ,
document.getElementById('app')
);
<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>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<div id="app"></div>
In the above snippet, I set out to show you how props can be used and provided to child components.
I hope this can help and clarify some of this issues you've been having

react-select can load async data

I'm trying to build a select component using react-select plugin.
In the process of implementing this project, I have some kind of tricky problem with that. Check out my source code here: https://codesandbox.io/s/j148r99695
The problem that I have is I want to fetch all genresList data from the server and mapping them to select component. But somehow or I do wrong something, It's not working. Please see source code above to help me.
I fetch data from Movies component. Its work well and I pass a props to FormFilter component: <FormFilter genresList={this.state.genres} />. And in the FormFilter component, I check this.props.genresList, it's available. But when I'm trying to assign it to FormFilter state and console.log("state", this.state.genres); that. It's empty. Anyone can tell me why?
Default react-select using value and label to display data to select component. But you know some cases we have to custom that. I try it out by using map to transform to other arrays. But It's the best way? How can I custom valueKey and labelKey.
I'm using react-select beta version2.
UPDATE: I was fixed my project. Please check out the link below. Somehow it's not working. I was commend inside source code.
https://codesandbox.io/s/moym59w39p
So to make it works I have changed the FormFilter.js implementation:
import React, { Component } from "react";
import * as Animated from "react-select/lib/animated";
import AsyncSelect from "react-select/lib/Async";
class FormFilter extends Component {
constructor(props) {
super(props);
this.state = {
inputValue: "",
selectedOption: "",
genres: []
};
}
selectGenreHandleChange = newValue => {
const inputValue = newValue.replace(/\W/g, "");
this.setState({ inputValue });
console.log(inputValue);
};
componentDidMount() {
this.genresOption();
}
filterGenres = inputValue => {
const genres = this.genresOption();
//HERE - return the filter
return genres.filter(genre =>
genre.label.toLowerCase().includes(inputValue.toLowerCase())
);
};
promiseOptions = inputValue => {
return new Promise(resolve => { // HERE - you have to return the promise
setTimeout(() => {
resolve(this.filterGenres(inputValue));
}, 1000);
});
};
genresOption() {
const options = [];
const genres = this.props.genresList.genres; //HERE - array is genres in genresList
if (genres && genres instanceof Array) {
genres.map(genre => options.push({ value: genre.id, label: genre.name}));
}
return options;
}
render() {
const { inputValue } = this.state;
if (this.state.genres) console.log("state", this.state.genres);
if (this.props.genresList)
console.log("Movies props", this.props.genresList);
return (
<div className="filter_form">
<span className="search_element full">
<label htmlFor="genres">Genres</label>
<AsyncSelect
className="select genres"
classNamePrefix="tmdb_select"
isMulti
isSearchable="true"
isClearable="true"
cacheOptions
components={Animated}
value={inputValue}
defaultOptions
onInputChange={this.selectGenreHandleChange}
loadOptions={this.promiseOptions}
/>
</span>
</div>
);
}
}
export default FormFilter;
I have write a comment "HERE - something" to let you know what I changed. There are not big problems :)
I did some changed in your FIDDLE and it's works for me
Something like
import React, {Component} from "react";
import { render } from 'react-dom';
import Movies from './Movies';
import "./styles.css";
class App extends Component {
render() {
return (
<div className="App">
<Movies />
</div>
);
}
}
let a = document.getElementById("root");
render(<App />, a);

Error dynamically updating an array list in ReactJs

I am learning reactJS and so I am trying my hands on an example. This example has a form textfield that can add an item to an existing array on click of a button. I am having errors here as when I enter a text and click on the button, the array list is not updated except I try to make changes to the text entered in the textfield. This is what I am doing:
import React, { Component } from 'react';
class App extends Component {
constructor(props){
super(props);
this.state = {
currentName : '',
arrays : ['john', 'james', 'timothy']
}
}
render() {
const showNames = this.state.arrays.map((thisName) => {
const values = <li>{thisName}</li>;
return values;
});
const getText = (e) => {
let value = e.target.value;
this.setState({
currentName : value
})
}
const addToUsers = () => {
this.state.arrays.push(this.state.currentName)
}
return (
<div>
<p>Add new name to List</p><br/>
<form>
<input type="text" onChange={getText}/>
<button type="button" onClick={addToUsers}>Add User</button>
</form>
<ul>
{showNames}
</ul>
</div>
);
}
}
export default App;
There are a host of things wrong with this, but your issue is likely that you need to use setState to modify state.
import React, { Component } from 'react';
class App extends Component {
constructor(){
super();
this.state = {
names: ['john', 'james', 'timothy']
}
}
addToUsers = () => {
this.setState(
prevState => ({
names: [...prevState.names, this.input.value]
})
)
}
render() {
const names = this.state.names.map(
(name, index) => <li key={index}>{name}</li>
)
return (
<div>
<p>Add new name to List</p><br/>
<form>
<input type="text" ref={e => this.input = e} />
<button type="button" onClick={this.addToUsers}>Add User</button>
</form>
<ul>
{names}
</ul>
</div>
)
}
}
export default App;
This quick edit changes a few things:
Uses setState for the addToUsers method
Eliminate onChange tracking and pull the name directly from the input when the button is clicked
Move the addToUsers method out to the component class rather than defining it on render
Rename this.state.arrays to this.state.names
Simplify conversion of this.state.names into list items
Set key on array elements (name list items)
Use prevState in setState to avoid race conditions
You need to make sure you update state using the setState method.
When you update arrays you are reaching into the state object and manipulating the data directly instead of using the method.
Instead try something like:
const addToUsers = () => {
const newArray = this.state.arrays.concat([this.state.currentName]);
this.setState({
arrays: newArray
});
}
You probably must add
onChange={getText}.bind(this)
to your functions.
Also change this
const addToUsers = () => {
this.state.arrays.push(this.state.currentName)
}
to this
const addToUsers = () => {
this.setState({here put your variable})
}

Categories

Resources