React not rendering list elements - reactjs

Problem
It's not showing the elements within the item. It doesn't render anything at the moment.
List.js
import React from 'react';
const List = props => (
<div>
{
props.items.map((item, index) => {
return <div key={index}>
<h1>{item.name}</h1>
<p>{item.term}</p>
</div>
})}
</div>
);
export default List;
App.js
import React, {Component} from 'react'
import './App.css'
import List from './List';
class App extends Component {
constructor(props) {
super(props);
this.state = {
term: '',
name: '',
items: []
};
}
onChange = (event) => {
const { name, value } = event.target;
this.setState({ [name]: value });
}
onSubmit = (event) => {
event.preventDefault();
this.setState({
term: '',
name: '',
items: [
...this.state.items,
this.state.term,
this.state.name
]
});
}
render() {
const { term, name, items } = this.state;
return (
<div>
<form className="App" onSubmit={this.onSubmit}>
<input name="term" value={this.state.term} onChange={this.onChange}/>
<input name="name" value={this.state.name} onChange={this.onChange}/>
<button>Submit</button>
</form>
<List items={this.state.items} />
</div>
);
}
}

Issue was in onSubmit, you need to convert to object and than add
onSubmit = event => {
event.preventDefault();
this.setState({
term: "",
name: "",
items: [...this.state.items, { term: this.state.term, name: this.state.name}]
});
setTimeout(() => { console.log(this.state.items) }, 0)
};
https://codesandbox.io/s/p7p128w7mx

Related

How to reset text field React Formik?

I have a container component that works with Redux, in it I determined the state of the text field and throw it into the form component that works with Formik
But I have such a problem that after clicking the button, the text field is not cleared
How do I fix this ? I've seen a lot about resetForm() from Formik, but I can't work with it
TaskInput.jsx
export const TaskInput = ({ value, onChange, onAdd }) => {
const formik = useFormik({
initialValues: {
text: value,
},
})
return (
<Container>
<Formik>
<FormWrapper onSubmit={onAdd}>
<Input
id="task"
type="text"
placeholder="Add a task"
value={formik.text}
onChange={onChange}
/>
<AddButton type="submit">Add</AddButton>
</FormWrapper>
</Formik>
</Container>
)
}
Task.jsx(component container)
class Task extends PureComponent {
constructor(props) {
super(props)
this.state = {
taskText: '',
}
}
handleInputChange = e => {
this.setState({
taskText: e.target.value,
})
}
handleAddTask = () => {
const { taskText } = this.state
const { addTask } = this.props
addTask(uuidv4(), taskText, false)
this.setState({
taskText: '',
})
}
render() {
const { taskText } = this.state
return (
<>
<TaskInput
onAdd={this.handleAddTask}
onChange={this.handleInputChange}
value={taskText}
/>
...
</>
)
}
}
const mapStateToProps = ({ tasks }) => {
return {
tasks,
}
}
export default connect(mapStateToProps, {
addTask,
removeTask,
completeTask,
editTask,
})(Task)
Solution
export const TaskInput = ({ value, onChange, onAdd }) => {
const inputRef = useRef(null)
const formik = useFormik({
initialValues: {
text: value,
},
})
const onSubmit = () => {
onAdd(),
inputRef.current.value = ''
}
return (
<Container>
<Formik>
<FormWrapper onSubmit={onSubmit}>
<Input
ref={inputRef}
id="task"
type="text"
placeholder="Add a task"
value={formik.text}
onChange={onChange}
/>
<AddButton type="submit">Add</AddButton>
</FormWrapper>
</Formik>
</Container>
)
}

React Api call: Displaying only 1st onSubmit but nothing after

I have two components, CryptoPrice with a coin prop which calls an API to get the price, and Nav where I search for a coin, and it renders the CryptoPrice component assigning the onSubmit value to CryptoPrice coin prop.
The display works good until I do a second onSubmit from the Nav. When I do a second onSubmit, nothing changes.
App.js code:
import CryptoPrice from "./components/CryptoPrice";
import Nav from "./components/Nav";
function App() {
return (
<div className="App">
<header className="App-header">
<h1>Crypto Prices</h1>
<div className="flex">
<CryptoPrice coin="bitcoin" />
<CryptoPrice coin="ethereum" />
</div>
<div>
<Nav></Nav>
</div>
</header>
</div>
);
}
CryptoPrice component:
import styles from "./css/CryptoPrice.module.css";
export default class CryptoPrice extends React.Component {
constructor(props) {
super(props);
this.state = {
price: [],
url: `https://api.coingecko.com/api/v3/simple/price?ids=${this.props.coin}&vs_currencies=usd`,
};
}
componentDidMount = () => {
this.loadData();
setInterval(this.loadData, 20000);
};
loadData = () => {
fetch(this.state.url)
.then((response) => response.json())
.then((data) => {
let key = Object.keys(data);
return data[key];
})
.then((coin) => {
let price = coin.usd;
this.setState({ price });
});
};
render() {
return (
<div className={styles.padding}>
<h2>{this.props.coin} price</h2>
<div>{this.state.price}$</div>
</div>
);
}
}
Nav Component
import CryptoPrice from "./CryptoPrice";
export default class Nav extends React.Component {
constructor(props) {
super(props);
this.state = {
coin: "",
isSubmitted: false,
};
}
componentDidMount() {
this.setState({ isSubmitted: false });
}
render() {
return (
<div>
<form
onSubmit={(e) => {
e.preventDefault();
this.setState({ isSubmitted: true });
}}
>
<input
type="text"
onChange={(e) => {
this.setState({ coin: e.target.value });
}}
></input>
<input type="submit" value="Add"></input>
</form>
{this.state.isSubmitted && <CryptoPrice coin={this.state.coin} />}
</div>
);
}
}
Thanks so much for any help/feedback
Your issue is because you are setting the url in state so it will not update when the props update. Try changing you fetch function to use props directly(also remember to clear the setInterval when you unmount):
loadData = () => {
fetch(`https://api.coingecko.com/api/v3/simple/price?ids=${this.props.coin}&vs_currencies=usd`)
.then((response) => response.json())
.then((data) => {
let key = Object.keys(data);
return data[key];
})
.then((coin) => {
let price = coin.usd;
this.setState({ price });
});
};

trouble setting state in reactjs

I have a very simple code to set a state variable but have trouble.
https://codesandbox.io/s/highcharts-react-demo-rtlie
I see the this.state.chart_options being displayed on the console but its null.
Code:_
import React from "react";
import { render } from "react-dom";
// Import Highcharts
import Highcharts from "highcharts/highstock";
import drilldow from "highcharts/modules/drilldown";
//import HighchartsReact from "./HighchartsReact.js";
import PieChart from "highcharts-react-official";
drilldow(Highcharts);
const options = {
chart: {
type: "pie"
},
series: [
{
data: [{ y: 100, name: "Female" }, { y: 50, name: "Male" }]
}
]
};
class App extends React.Component {
constructor() {
super();
this.state = {
chart_options: null
};
this.setState({ chart_options: options }, () => {
console.log("this.state - ", this.state);
console.log("options - ", options);
});
}
onFilterClickHandler = () => {
console.log("hi", this.state.chart_options);
};
render() {
const key = 1;
return (
<div>
<div>
<label>
<input
type="checkbox"
value={key}
onClick={this.onFilterClickHandler}
/>
</label>
</div>
<h2>Highcharts</h2>
<PieChart highcharts={Highcharts} options={options} />
</div>
);
}
}
render(<App />, document.getElementById("root"));
You're calling setState inside the constructor (when the component is not yet mounted).
To get the expected console.log output, call setState from the lifecycle method componentDidMount:
class App extends React.Component {
constructor() {
super();
this.state = {
chart_options: null
};
}
componentDidMount() {
this.setState({ chart_options: options }, () => {
console.log("this.state - ", this.state);
console.log("options - ", options);
});
console.log(this.state);
}
onFilterClickHandler = () => {
console.log("hi", this.state.chart_options);
};
render() {
const key = 1;
return (
<div>
<div>
<label>
<input
type="checkbox"
value={key}
onClick={this.onFilterClickHandler}
/>
</label>
</div>
<h2>Highcharts</h2>
<PieChart highcharts={Highcharts} options={options} />
</div>
);
}
}

Deleting item on to do list

I want to add a delete button to my 'Todo' component
also make a method called deleteTodo in my 'App' component
pass the deleteTodo method to the 'ToDo' component as a prop
and finally add an onClick event listener to the delete button
I've been stuck for days trying to figure this out any help would be much appreciated
my Todo.js component
import React, { Component } from 'react';
class ToDo extends Component {
render() {
return (
<li>
<input type="checkbox" checked={ this.props.isCompleted } onChange={ this.props.toggleComplete } />
<span>{ this.props.description }</span>
<button> </button>
</li>
);
}
}
export default ToDo;
the App
import React, { Component } from 'react';
import './App.css';
import ToDo from './components/ToDo.js';
class App extends Component {
constructor(props) {
super(props);
this.state = {
todos: [
{ description: 'Walk the cat', isCompleted: true },
{ description: 'Throw the dishes away', isCompleted: false },
{ description: 'Buy new dishes', isCompleted: false }
],
newTodoDescription: ''
};
}
deleteTodo() {}
handleChange(e) {
this.setState({ newTodoDescription: e.target.value })
}
handleSubmit(e) {
e.preventDefault();
if (!this.state.newTodoDescription) { return }
const newTodo = { description: this.state.newTodoDescription, isCompleted: false };
this.setState({ todos: [...this.state.todos, newTodo], newTodoDescription: '' });
}
toggleComplete(index) {
const todos = this.state.todos.slice();
const todo = todos[index];
todo.isCompleted = todo.isCompleted ? false : true;
this.setState({ todos: todos });
}
render() {
return (
<div className="App">
<ul>
{ this.state.todos.map( (todo, index) =>
<ToDo key={ index } description={ todo.description } isCompleted={ todo.isCompleted } toggleComplete={ () => this.toggleComplete(index) } />
)}
</ul>
<form onSubmit={ (e) => this.handleSubmit(e) }>
<input type="text" value={ this.state.newTodoDescription } onChange={ (e) => this.handleChange(e) } />
<input type="submit" />
</form>
</div>
);
}
}
export default App;
Change all your event handler functions to arrow functions or bind them manually in constructor otherwise you can’t do setState or access to props inside these functions
You already have deleteTodo function declared in App.js Component so pass down this function as prop to Todo component.
Call deleteTodo function on button onClick using this.props.deleteTodo by passing description as parameter. You will use this description to remove todo from todos list in deleteTodo function
Now, when button is clicked you need to delete an item so do filter on todos state array with description
And set the newly returned todos to your state so that you will see only available todos
Here is updated code
App.js
import React, { Component } from 'react';
import './App.css';
import ToDo from './components/ToDo.js';
class App extends Component {
constructor(props) {
super(props);
this.state = {
todos: [
{ description: 'Walk the cat', isCompleted: true },
{ description: 'Throw the dishes away', isCompleted: false },
{ description: 'Buy new dishes', isCompleted: false }
],
newTodoDescription: ''
};
}
deleteTodo = description => {
const newTodos = this.state.todos.filter(todo => todo.description != description);
this.setState({
todos: newTodos
});
}
handleChange = e => {
this.setState({ newTodoDescription: e.target.value })
}
handleSubmit = e => {
e.preventDefault();
if (!this.state.newTodoDescription) { return }
const newTodo = { description: this.state.newTodoDescription, isCompleted: false };
this.setState({ todos: [...this.state.todos, newTodo], newTodoDescription: '' });
}
toggleComplete = index => {
const todos = this.state.todos.slice();
const todo = todos[index];
todo.isCompleted = todo.isCompleted ? false : true;
this.setState({ todos: todos });
}
render() {
return (
<div className="App">
<ul>
{ this.state.todos.map( (todo, index) =>
<ToDo key={ index } description={ todo.description } isCompleted={ todo.isCompleted } toggleComplete={ () => this.toggleComplete(index) } deleteTodo={this.deleteTodo} />
)}
</ul>
<form onSubmit={ (e) => this.handleSubmit(e) }>
<input type="text" value={ this.state.newTodoDescription } onChange={ (e) => this.handleChange(e) } />
<input type="submit" />
</form>
</div>
);
}
}
export default App;
Todo.js
import React, { Component } from 'react';
class ToDo extends Component {
render() {
return (
<li>
<input type="checkbox" checked={ this.props.isCompleted } onChange={ this.props.toggleComplete } />
<span>{ this.props.description }</span>
<button onClick={() => this.props.deleteTodo(this.props.description)}>Delete Todo </button>
</li>
);
}
}
export default ToDo;
Define your deleteToDo method in app component and pass it down to ToDo component as follows.
<ToDo
key={ index }
description={ todo.description }
isCompleted={ todo.isCompleted }
toggleComplete={ () => this.toggleComplete(index) }
deleteToDo={this.deleteToDo}
/>
Then inside your ToDo component you can add the handler as
<button onClick={this.props.deleteToDo}> </button>
Hope this solves your query!!

this.props is not a function with redux connect

I am getting the error that this.props.onAddTrip on components/NewTrip.jsx is not a function and I have been debugging this for a few days. Any help is appreciated! I have included the code to my NewTrip component, addTrip container and my redux action.
components/NewTrip.jsx
import React from 'react';
import ReactDOM from 'react-dom';
class NewTrip extends React.Component {
constructor(props) {
super(props)
this.state = {
location: '',
start: '',
end: ''
}
this.handleInputChange = this.handleInputChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
this.handleReset = this.handleReset.bind(this)
}
handleInputChange(e){
this.setState({
[e.target.name]: e.target.value
});
};
handleSubmit(e) {
e.preventDefault();
if(this.state.location.trim() && this.state.start.trim() &&
this.state.end.trim()) {
this.props.onAddTrip(this.state);
this.handleReset();
}
};
handleReset(){
this.setState({
location: '',
start: '',
end: ''
});
};
render() {
return (
<div className="container">
<form className="add_trip" onSubmit={ this.handleSubmit }>
<input name="location" className="start_form" type="text" autocomplete="off" placeholder=" Location" onChange={ this.handleInputChange } value={ this.state.location }/>
<input name="start" type="date" onChange={ this.handleInputChange } value={ this.state.start }/>
<input name="end" type="date" onChange={ this.handleInputChange } value={ this.state.end }/>
<input className="end_form" type="submit" value="Add" />
</form>
</div>
)
}
}
export default NewTrip;
containers/addTrip.js
import React from 'react';
import { connect } from 'react-redux';
import { addTrip } from '../actions';
import NewTrip from '../components/NewTrip.jsx';
const mapDispatchToProps = dispatch => {
return {
onAddTrip: trip => {
dispatch(addTrip(trip));
}
};
};
export default connect(
null,
mapDispatchToProps
)(NewTrip);
actions/index.js
import axios from 'axios';
export const addTrip = ( {location, start, end} ) => {
return (dispatch) => {
return axios.post('/api/trips', { location, start, end})
.then(response => {
dispatch(addTripSuccess(response.data))
})
.catch(error => {
throw(error)
})
}
}
export const addTripSuccess = data => {
return {
type: 'ADD_TRIP',
payload: {
// id: data.row.split(",")[0].substring(1),
id: data._id,
location: data.location,
start: data.start,
end: data.end
}
}
}
I'm a little confused about the purpose of the addTrip.js. If you move the logic in that file into your NewTrip component, your function should be defined.
import React from 'react';
import ReactDOM from 'react-dom';
import { connect } from 'react-redux'
import { addTrip } from '../actions'
class NewTrip extends React.Component {
constructor(props) {
super(props)
this.state = {
location: '',
start: '',
end: ''
}
this.handleInputChange = this.handleInputChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
this.handleReset = this.handleReset.bind(this)
}
handleInputChange(e){
this.setState({
[e.target.name]: e.target.value
});
};
handleSubmit(e) {
e.preventDefault();
if(this.state.location.trim() && this.state.start.trim() &&
this.state.end.trim()) {
this.props.addTrip(this.state);
this.handleReset();
}
};
handleReset(){
this.setState({
location: '',
start: '',
end: ''
});
};
render() {
return (
<div className="container">
<form className="add_trip" onSubmit={ this.handleSubmit }>
<input name="location" className="start_form" type="text" autocomplete="off" placeholder=" Location" onChange={ this.handleInputChange } value={ this.state.location }/>
<input name="start" type="date" onChange={ this.handleInputChange } value={ this.state.start }/>
<input name="end" type="date" onChange={ this.handleInputChange } value={ this.state.end }/>
<input className="end_form" type="submit" value="Add" />
</form>
</div>
)
}
}
export default connect(null, { addTrip })(NewTrip);
Give that a try. Also changed this.props.onAddTrip to this.props.addTrip (in handleSubmit) since thats the name of your imported function.

Resources