React and checking condition after passing function through props - javascript

I'm fighting with my app since long time and slowly there is progress however I have still problem with one thing
I want to pass function thought props from Form Component to List component, after that I wish to check if button add was clicked if yes then I wish to launch function getMovie() inside List component and send another request to json database. with edit and remove it works as there are in same component, with adding button it is a bit more tricky.
the problem is that if I write just
else if (this.props.addClick) {
this.getMovie();
}
it's keep sending requests to database over and over
below is my code
Form Component
class Form extends React.Component {
constructor(props) {
super(props)
this.state = {
name: '',
type: '',
description: '',
id: '',
movies: [],
errors: "",
}
}
handleSubmit = e => {
e.preventDefault()
const url = `http://localhost:3000/movies/`;
if (this.state.name != "" && this.state.type != "" && this.state.description != "") {
axios
.post(url, {
name: this.state.name,
type: this.state.type,
description: this.state.description,
id: this.state.id,
})
.then(res => {
this.setState({
movies: [this.state.name, this.state.type, this.state.description, this.state.id]
})
})
.then(this.setState({
isButtonRemoveClicked: true
}))
}
else {
this.setState({
errors:"Please, Fill all forms above"
})
}
}
return (
<div>
<form onSubmit={this.handleSubmit}>
<input type="text" placeholder="Movie" onChange={this.handleChangeOne}/>
<input type="text" placeholder="Type of movie" onChange={this.handleChangeTwo}/>
<textarea placeholder="Description of the movie"
onChange={this.handleChangeThree}></textarea>
<input id="addMovie" type="submit" value="Add movie" ></input>
<p>{this.state.errors}</p>
</form>
<List removeClick={this.handleRemove} editClick={this.editMovie} addClick={this.handleSubmit}/>
</div>
)
List Component
class List extends React.Component {
constructor(props) {
super(props)
this.state = {
movies: [],
isButtonRemoveClicked: false,
}
}
componentDidMount() {
this.getMovie()
}
componentDidUpdate() {
if (this.state.isButtonRemoveClicked === true) {
this.getMovie();
this.timer = setTimeout(() => {
this.setState({
isButtonRemoveClicked: false
})
}, 10)
}
else if (this.props.addClick === true) {
this.getMovie();
}
}
componentWillUnmount() {
clearTimeout(this.timer)
}
getMovie = () => {
const url = `http://localhost:3000/movies`;
axios
.get(url)
.then(res => {
const movies = res.data;
this.setState({
movies: movies,
})
})
.catch((err) => {
console.log(err);
})
}

There is nothing magical ;)
You're start loading data from componentDidUpdate() ... data loads, componentDidUpdate is fired again, again...
Don't handle events this way.

If your main objective is to call function in child component from parent component, then you can use refs.
Example in your code :-
class Form extends React.Component {
constructor(props) {
super(props)
this.state = {
name: '',
type: '',
description: '',
id: '',
movies: [],
errors: "",
}
}
handleSubmit = e => {
e.preventDefault()
const url = `http://localhost:3000/movies/`;
if (this.state.name != "" && this.state.type != "" && this.state.description != "") {
axios
.post(url, {
name: this.state.name,
type: this.state.type,
description: this.state.description,
id: this.state.id,
})
.then(res => {
this.setState({
movies: [this.state.name, this.state.type, this.state.description, this.state.id]
})
})
.then(
this.list.getMovie(); // call child function here
this.setState({
isButtonRemoveClicked: true
}))
}
else {
this.setState({
errors:"Please, Fill all forms above"
})
}
}
return (
<div>
<form onSubmit={this.handleSubmit}>
<input type="text" placeholder="Movie" onChange={this.handleChangeOne}/>
<input type="text" placeholder="Type of movie" onChange={this.handleChangeTwo}/>
<textarea placeholder="Description of the movie"
onChange={this.handleChangeThree}></textarea>
<input id="addMovie" type="submit" value="Add movie" ></input>
<p>{this.state.errors}</p>
</form>
<List
ref={list => this.list=list } // Create ref here
removeClick={this.handleRemove}
editClick={this.editMovie}
addClick={this.handleSubmit}/>
</div>
)
And in list component no need to use componentDidUpdate getMovie() call.
class List extends React.Component {
constructor(props) {
super(props)
this.state = {
movies: [],
isButtonRemoveClicked: false,
}
}
componentDidMount() {
this.getMovie()
}
getMovie = () => {
const url = `http://localhost:3000/movies`;
axios
.get(url)
.then(res => {
const movies = res.data;
this.setState({
movies: movies,
})
})
.catch((err) => {
console.log(err);
})
}

I think you are handling events in an overcomplicated manner. Why don't you lift props from inside the List component and just trigger the desired behaviour in the Form?. For example:
class List extends React.Component {
handleAddClick() {
this.props.onAddClick()
}
handleEditClick() {
this.props.onEditClick()
}
handleRemoveClick() {
this.props.onRemoveClick()
}
render() {
return (
<div>
<button onClick={() => this.handleAddClick()}>Add</button>
<button onClick={() => this.handleEditClick()}> Edit</button>
<button onClick={() => this.handleRemoveClick()} > Remove</button>
</div>
})
}
and
class Form extends React.Component {
getMovie() {
// Make AXIOS request
}
handleAdd() {
this.getMovie();
}
handleRemove() {
// REMOVE CODE
}
handleEdit() {
// EDIT CODE
}
render() {
<form>
{/* Form elements */}
<List
onAddClick={() => this.handleAdd()}
onRemoveClick={() => this.handleRemove()}
onEditClick={() => this.handleEdit()}
/>
</form>
}
}

Related

React:How to Select All/UnSelect All checkboxes on button click?

I am displaying data in the checkboxlist. How to implement Select All and UnselectAll buttons that selects all or unselect all checkboxes. Please find my react code and data coming from api.
[ {"templateID":"11","templateName":"All” },
{"templateID":"21","templateName":"SC" }]
import React from "react";
export class Delete_Item extends React.Component {
constructor(props) ;
super(props);
this.state = {
Template_ID: "",
TemplateName: "",
Templatelist: [],
checkedItems: [],
};
this.handleChange = this.handleChange.bind(this);
}
componentDidMount() {
this.getTemplateList();
}
getTemplateList() {
fetch(REQUEST_URL, { credentials: 'include' })
.then(response => response.json())
.then((data) => {
this.setState({
Templatelist: data,
TemplateName: data[0].templateName,
loading: false
})
console.log(this.state.Templatelist);
})
}
handleChange(event) {
const id = parseInt(event.target.id, 10);
const index = this.state.checkedItems.indexOf(id);
const updatedArray = [...this.state.checkedItems];
if (index !== -1) {
updatedArray.splice(index, 1);
} else {
updatedArray.push(id);
}
this.setState((prevState) => ({
checkedItems: updatedArray
}));
console.log(this.state.checkedItems);
}
render() {
return (
<div>
<ul style={{ listStyle: 'none' }} >
{
(this.state.Templatelist.map((item, index) => {
return (
<li key={item.templateID}>
<input type="checkbox" id={item.templateID} value={item.templateName}
onChange={this.handleChange} />
{item.templateName}</li>)}))}</ul>
<input type="button" name="SelectAll" value="Select All" />
<input type="button" name="UnSelectAll" value="Clear All" />
</div>
);
}
}
export default Delete_Item;
thanks
selectAll() {
return this.setState({ checkedItems: this.state.Templatelist.map(id => id) });
}
unselectAll() {
return this.setState({ checkedItems: [] });
}

React data from Children to Parent, map json

I'm learning ReactJS and I want to map a json in a father component from child search bar. So I got this:
export default class Child extends Component {
constructor(props) {
super(props)
this.state = { data:[], value: '' };
this.handleSubmit = this.handleSubmit.bind(this)
this.handleChange = this.handleChange.bind(this)
}
guardar = (data) => {
this.setState({ data })
this.props.parentCallback({ data })
}
handleChange(e) {
this.setState({ value: e.target.value })
axios.get(`http://localhost:3001/api/search?query=${ e.target.value }`)
.then(( { data } ) => this.guardar(data) )
}
handleSubmit(e) {
e.preventDefault()
}
render() {
return(
<form onSubmit={this.handleSubmit}>
<input type="text"
name='searchbar'
onChange={this.handleChange}/>
</form>
)
}
}
export default class Parent extends Component {
state = {
data: [],
}
handleCallback = (childData) => {
this.setState({
data: childData
})
console.log(this.state.data);
}
render() {
const { data } = this.state
return(
<div>
<SearchBar parentCallback = {this.handleCallback}/>
<ProductCard />
{ [data].map( res => <li key={res.id}>{ res.title }</li>) }
</div>
)
}
}
Here is the result:
I want to map if even if the array is empty, in the console shows me the 50 elements only if I write more than twice in the input and I want them when I reload the page.
Beforehand thank you very much!!

React class component. Todo app. How to store data with localStorage

I'd like to store todo data with localStorage so that it won't disappear after refreshing the page.
I used React class component when started creating.
I've added 'handleFormSubmit' and 'ComponentDidMount' methods.
nothing stores in localStorage when I type todo and choose date.
get an error in ComponentDidMount with
Line 'const result = localData ? JSON.parse(localData) : [];'
:SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data
how can I set and get items?
It would be really appreciated if I could get help.
I'd like to make this app really work.
import React from "react"
import TodoItem from "./components/TodoItem"
import todosData from "./components/todosData"
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
todos: todosData,
//setTodos: todosData,
newItem: "",
deadline: "",
editing: false
}
this.handleChange = this.handleChange.bind(this)
this.addTodo = this.addTodo.bind(this)
this.updateInput = this.updateInput.bind(this)
this.deleteItem = this.deleteItem.bind(this)
this.updateItem = this.updateItem.bind(this)
this.updateDeadline = this.updateDeadline.bind(this)
this.updateInputDeadline = this.updateInputDeadline.bind(this)
this.editItem = this.editItem.bind(this)
this.handleFormSubmit = this.handleFormSubmit.bind(this)
}
handleChange(id) {
this.setState((prevState) => {
const updatedTodos = prevState.todos.map((todo) => {
if (todo.id === id) {
return { ...todo, completed: !todo.completed };
} else {
return todo;
}
});
return { todos: updatedTodos };
});
}
addTodo(e) {
e.preventDefault();
const newTodo = {
id: this.state.todos.length + 1,
text: this.state.newItem,
completed: false,
deadline: this.state.deadline
}
const newTodos = this.state.todos.concat([newTodo]);
this.setState({
todos: newTodos
})
}
updateInput(value, id) {
this.setState((prevState) => {
const updatedTodos = prevState.todos.map((todo) => {
if(todo.id === id) {
return {...todo, text: value}
}else {
return todo;
}
})
return {todos: updatedTodos}
})
}
updateInputDeadline(value, id) {
this.setState((prevState) => {
const updatedTodos = prevState.todos.map((todo) => {
if(todo.id === id) {
console.log(value, id);
return {...todo, deadline: value}
}else {
return todo;
}
})
return {todos: updatedTodos}
})
}
updateItem(e) {
this.setState({
newItem: e.target.value
})
}
updateDeadline(e) {
this.setState({
deadline: e.target.value
})
}
deleteItem(id){
const filteredItems= this.state.todos.filter(item =>
item.id!==id);
this.setState({
todos: filteredItems
})
}
editItem(id) {
this.setState({
editing: id
})
}
handleFormSubmit() {
const { todo, deadline } = this.state;
localStorage.setItem('todo', JSON.stringify(todo));
localStorage.setItem('deadline', deadline);
};
componentDidMount() {
const localData = localStorage.getItem('todo');
const result = localData ? JSON.parse(localData) : [];
const deadlineData = localStorage.getItem('deadline');
this.setState({ result, deadlineData });
}
render() {
const todoItems = this.state.todos.map
(item =>
<TodoItem
key={item.id}
item={item}
handleChange={this.handleChange}
addTodo={this.addTodo}
deleteItem={this.deleteItem}
updateInput={this.updateInput}
updateInputDeadline={this.updateInputDeadline}
isEdited={this.state.editing === item.id}
editItem={this.editItem}
/>)
return (
<div className="todo-list">
<Timer />
<form onSubmit={this.handleFormSubmit}>
<div className="add-todo">
<label>Add an item...</label>
<input
type="text"
name="todo"
placeholder="Type item here..."
value={this.state.newItem}
onChange={this.updateItem}
/>
</div>
<div className="date">
<label htmlFor="deadline">Deadline</label>
<input
type="date" id="start" name="deadline"
min="2021-01-01"
max="2024-12-31"
value={this.state.deadline}
onChange={this.updateDeadline}
/>
</div>
<button type="submit" onClick={this.addTodo}>Add to the list</button>
</form>
{todoItems.length === 0 ? <p>No items</p> : null}
<div className="todoitems">
{todoItems}
</div>
</div>
)
}
}
export default App
When you press the button, there are two events that you are trying to call - addTodo and handleFormSubmit. Since you are calling e.preventDefault() in addTodo, the submit event is never called. You could do all of the actions you need in one of the methods.
My guess is that you are either trying to JSON.parse an array instead of an object, or the value of todo is undefined. You are trying to get todo out of this.state, but you only have todos in your state, so it might be a typo. The same goes for deadline.
You are doing the setting and getting correctly. You could actually get data from localStorage even when you are first setting the state in constructor. But the componendDidMount approach you tried is also good.
constructor(props) {
super(props)
const cachedTodos = localStorage.getItem("todo")
this.state = {
todos: cachedTodos ?? todosData,
...
}

Updated state is not passed as props to component after setState()

I have a main react component called 'App' which contain user input data in its state. The state is updated with setState() every time user enter new data. Then the state is passed as props to another component called 'IncomeList' which render the data on screen. However the IncomeList component is not getting updated state after user input some data.
class App extends React.Component {
constructor(props) {
super(props);
this.addData = this.addData.bind(this);
this.state = {
expenses: [],
income: [],
}
}
addData(data) {
if (data.type == 'income') {
this.setState((prevState) => {
income: prevState.income.push(data);
}, console.log(this.state.income));
} else if (data.type == 'expense') {
this.setState((prevState) => {
expenses: prevState.expenses.push(data);
})
}
}
render() {
return (
<div>
<UserInput addData={this.addData} />
<IncomeList income={this.state.income} />
</div>
);
}
}
// UserInput component which contain a form
class UserInput extends React.Component {
constructor(props) {
super(props);
this.addDataLocal = this.addDataLocal.bind(this);
}
addDataLocal(e) {
e.preventDefault();
const data = {
type: e.target.elements.type.value,
description: e.target.elements.description.value,
amount: e.target.elements.amount.value
}
this.props.addData(data);
}
render() {
return (
<div>
<form onSubmit={this.addDataLocal}>
<select name="type" id="input-type" name="type">
<option value="income">Income</option>
<option value="expense">Expense</option>
</select>
<input type="text" placeholder="decription..." name="description"/>
<input type="number" placeholder="amount..." name="amount"/>
<input type="submit" value="Add"/>
</form>
</div>
)
}
}
class IncomeList extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
{
this.props.income.map((item) => {
return (
<IncomeListItem key={item.amount} item={item}/>
)
})
}
</div>
)
}
}
You don't return anything from this.setState. You need return an object to be merged with your current state.
addData(data) {
const key = data.type === 'income' ? 'income' : 'expenses';
this.setState(prevState => ({
// with Computed Property Names we can make our
// code concise and avoid conditionals
[key]: [...prevState[key], data]
}), console.log(this.state[key]));
}
your addData should be like this
addData(data) {
if (data.type == 'income') {
let income=[...this.state.income];
income.push(data);
this.setState({
income:income
})
} else if (data.type == 'expense') {
let expenses=[...this.state.expenses];
expenses.push(data);
this.setState({
expenses:expenses
});
}
}
With #Asaf Aviv input I have created a working fiddle. Hope this will help.
JS Fiddle

Automatically render child component when state has been updated in parent component

The parent component Dashboard holds the state for every ListItem I add to my Watchlist. Unfortunately, every time I am adding an Item, it gets added to the DB, but only shows up when I refresh the browser.
class UserDashboard extends React.Component {
state = {
data: []
}
componentWillMount() {
authService.checkAuthentication(this.props);
}
isLoggedIn = () => {
return authService.authenticated()
}
getAllCoins = () => {
//fetches from backend API
}
addWishlist = () => {
this.getAllCoins()
.then(things => {
this.setState({
data: things
})
})
console.log("CHILD WAS CLICKED")
}
componentDidMount() {
this.getAllCoins()
.then(things => {
this.setState({
data: things
})
})
}
render() {
return (
<div className="dashboard">
<h1>HI, WELCOME TO USER DASHBOARD</h1>
<SearchBar
addWishlist={this.addWishlist}
/>
<UserWatchlist
data={this.state.data}
/>
</div>
);
}
}
The User Watchlist:
class UserWatchlist extends React.Component {
constructor(props) {
super(props)
}
// componentDidUpdate(prevProps) {
// if (this.props.data !== prevProps.data) {
// console.log("CURRENT", this.props.data)
// console.log("PREVs", prevProps.data)
// }
// }
render() {
return (
<div>
<h2>These are tssssyou are watching:</h2>
<ul className="coin-watchlist">
{
this.props.data.map((coin, idx) => {
return <ListItem key={idx}
coin={coin.ticker}
price={coin.price}
/>
})
}
</ul>
</div>
)
}
}
The search Bar that shows potential Items to watch over:
class SearchBar extends React.Component {
constructor(props) {
super(props)
this.state = {
coins: [],
searchValue: ""
}
}
searchHandler = e => {
e.preventDefault()
const value = e.target.value
this.setState({
searchValue: value
});
if (value === "") {
this.setState({
coins: []
})
} else {
this.getInfo()
}
}
getInfo = () => {
// Searches the API
}
addWishlist = () => {
this.props.addWishlist();
}
render() {
const {coins, searchValue} = this.state
return (
<div className="coin-search">
<form>
<input
type="text"
className="prompt"
placeholder="Search by ticker symbol"
value={searchValue}
onChange={this.searchHandler}
/>
</form>
<ul className="search-suggestions">
{
coins.filter(searchingFor(searchValue)).map( coin =>
<Currency
coin={coin}
addWishlist={this.addWishlist}
/>
)
}
</ul>
</div>
);
}
}
And the actual Currency that gets clicked to be added:
class Currency extends React.Component {
addToWatchlist = () => {
// POST to backend DB to save
};
fetch("/api/add-coin", settings)
.catch(err => {
return err
})
}
clickHandler = () => {
this.addToWatchlist()
this.props.addWishlist()
}
render() {
return(
<div className="search-results">
<li>
<h3> { this.props.coin.currency } </h3>
<button
className="add-to-list"
onClick={this.clickHandler}
>
+ to Watchlist
</button>
</li>
</div>
)
}
}
As you can see, I am sending props down all the way down to child. When I click the button to Add to Watchlist, I see the console.log message appear, saying "CHILD WAS CLICKED". I've even tried just calling the method to fetch from backend API again.
Also, in UserWatchlist, I've tried a componentDidUpdate, but both prevProps and this.props show the very same array of data. Somewhere in the chain, my data is getting lost.
This is also my first time posting a question here, so if it can be improved, I am happy to add extra details and contribute something to this community
You probably forgot to wait for addToWatchlist to complete:
addToWatchlist = () => {
// POST to backend DB to save
return fetch("/api/add-coin", settings)
.catch(err => {
return err
})
}
clickHandler = () => {
this.addToWatchlist().then(() => {
this.props.addWishlist()
})
}

Categories