I just found out about reactjs, I do not understand why my code is not error but it can not render the data to view.
I tried the test function to display. it works normally,
But in the getAllProducts function, after calling the api, it seems impossible to update the html code on my page.
What was wrong with me?
Here is my code:
import React from 'react';
class ListObject extends React.Component {
getAllProducts() {
fetch("http://5bd054ce142d360013a172f3.mockapi.io/api/products")
.then(res => res.json())
.then((result) => {
// return (<h1>Why not display????</h1>);
result.map(
(product, i) => {
return <TableRow key={i} data={product} />
}
)
},
(error) => {
return "errrr";
}
)
}
test() {
return (<h1>Hello World</h1>);
}
render() {
return (
<div className="container-fluid">
<table className="table table-hover">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Avatar</th>
<th>Created At</th>
</tr>
</thead>
<tbody>
{this.getAllProducts()}
</tbody>
</table>
{this.test()}
</div>
);
};
}
class TableRow extends React.Component {
render() {
return (
<tr>
<td>{this.props.data.id}</td>
<td>{this.props.data.name}</td>
<td>{this.props.data.avatar}</td>
<td>{this.props.data.createdAt}</td>
</tr>
);
};
}
export default ListObject
You seem to have got it all wrong. In React, you need to render data based on component's state and props. So, you must do something like this:
class YourComponent extends React.Component {
getAllProducts() {
// you can handle a "loading" state as well
this.setState({isLoading: true});
fetch("http://example.com/api/products")
.then(res => res.json())
.then(
(result) => {
this.setState({
products: result,
isLoading: false,
});
},
(error) => {
return this.setState({hasError: true, error})
}
);
}
}
componentDidMount() {
fetchAllProducts();
}
render() {
const {products, isLoading, hasError} = this.state;
if (hasError) {
return (
<p>Something bad happened</p>
);
}
if (isLoading) {
return (
<p>Hey, we're fetching data...</p>
);
}
return (
<table>
{products.map(p => <TableRow ... />)
</table>
)
}
}
NOTE: I've used a few concepts that you should know about, so here are some docs:
Read about componentDidMount() here
We can declare special methods on the component class to run some code when a component mounts and unmounts. The componentDidMount() method runs after the component output has been rendered to the DOM.
Read about state here
Change your getAllProducts and add state object to the component like mentioned below. API call is asynchronous so you cannot return it directly. what you can do is use component state. And make the api call in componentDidMount to get the api data.
class ListObject extends React.Component {
state = {
result: []
};
componentDidMount() {
this.getAllProducts();
}
getAllProducts() {
return fetch("https://5bd054ce142d360013a172f3.mockapi.io/api/products")
.then(res => res.json())
.then(result => {
// return (<h1>Why not display????</h1>);
this.setState({
result
});
})
.catch(e => {
//dispatch some action to showcase error or
//make state update using setState to show error
return null;
});
}
getProductListUI = () => {
const { result } = this.state;
return result.map((product, i) => <TableRow key={i} data={product} />);
};
test() {
return <h1>Hello World</h1>;
}
render() {
return (
<div className="container-fluid">
<table className="table table-hover">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Avatar</th>
<th>Created At</th>
</tr>
</thead>
<tbody>{this.getProductListUI()}</tbody>
</table>
{this.test()}
</div>
);
}
}
class TableRow extends React.Component {
render() {
return (
<tr>
<td>{this.props.data.id}</td>
<td>{this.props.data.name}</td>
<td>{this.props.data.avatar}</td>
<td>{this.props.data.createdAt}</td>
</tr>
);
}
}
ReactDOM.render(<ListObject />, document.getElementById("root"));
Here is the codepen link working : working codepen link
Feedbacks welcome Thanks
Related
function App()
{
const [rows_data, SET_rows_data] = useState([]);
useEffect(() => {
get_rows_data(SET_rows_data); //Fetches the data. Runs only once
}, []);
useEffect(() => {
console.log(rows_data); //This rightly trigggers twice, and shows an empty array and the full array once fetched
}, [rows_data]);
return (
<div>
{Table_Renderer(rows_data)}
</div>
); //Doesn't re-render when rows_data changes
}
function get_rows_data(SET_rows_data)
{
fetch(...)
.then((res) => res.json())
.then((data) => {
SET_rows_data(data["rows"]); //Required data is within the "rows" object
});
}
function Table_Renderer(rows_data)
{
return (
<table>
<thead>
...
</thead>
<tbody >
{rows_data.map(row => {
return (
<tr>
...
</tr>
)
})}
</tbody>
</table>
)
}
export default (App);
I've added comments above to explain the issue.
The console.log in the useEffect, triggers twice as expected. But the 2nd render doesn't happen, even though the data in the JSX has changed.
If I re-save my work while 'npm start' is running, the data is properly displayed though
Hi #Dalhousie,
As you describe above, The Table_Renderer component is not re-rendering when the rows_data state changes. Its obvious behavior
Because Table_Renderer component does not have access to the rows_data state, and so it is not aware that the state has changed.
function App() {
const [rows_data, SET_rows_data] = useState([]);
useEffect(() => {
get_rows_data(SET_rows_data); // Fetches the data. Runs only once
}, []);
return (
<div>
<Table_Renderer rowsData={rows_data} />
</div>
);
}
function Table_Renderer({ rowsData }) {
return (
<table>
<thead>
...
</thead>
<tbody>
{rowsData.map(row => {
return (
<tr>
...
</tr>
);
})}
</tbody>
</table>
);
}
The delete function of my app is working fine, however it requires the user to manually refresh the page after the user click the delete button in order to see the new list of elements in my database. I would like to automatically refresh after the click event. I am using react hooks for this projects. However, I found one solution if I remove useEffect's [] but in my backend it shows, its requesting crazily. I don't know, is it wise to remove useffect's [ ]?
Here is the component where it fetches data from backend and passes the props to another component
import React, { useState, useEffect } from "react";
import axios from "axios";
import Table from "../Table/Table";
import "./Display.css";
const Display = () => {
const [state, setState] = useState({ students: [], count: "" });
const [searchItem, setsearchItem] = useState({
item: ""
});
const Search = e => {
setsearchItem({ item: e.target.value });
};
useEffect(() => {
axios
.get("/students")
.then(response => {
setState({
students: response.data.students,
count: response.data.count
});
})
.catch(function(error) {
console.log(error);
});
}, []); //If I remove this square brackets, it works
const nameFilter = state.students.filter(list => {
return list.name.toLowerCase().includes(searchItem.item.toLowerCase());
});
return (
<div>
<h3 align="center">Student tables</h3>
<p align="center">Total students: {state.count}</p>
<div className="input-body">
<div className="row">
<div className="input-field col s6">
<input placeholder="search student" onChange={Search} />
</div>
</div>
</div>
<table className="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Date of birth</th>
<th>Address</th>
<th>Zipcode</th>
<th>City</th>
<th>Phone</th>
<th>Email</th>
<th colSpan="2">Action</th>
</tr>
</thead>
{nameFilter.map((object, index) => {
return (
<tbody key={index}>
<Table obj={object} /> //In here I am passing the props to the another component.
</tbody>
);
})}
</table>
</div>
);
};
export default Display;
This is second component which receives the props.
import React, { useState } from "react";
import { Link } from "react-router-dom";
import axios from "axios";
const Table = props => {
const removeData = () => {
axios
.delete("/students/" + props.obj.id)
.then(console.log("Deleted"))
.catch(err => console.log(err));
};
return (
<React.Fragment>
<tr>
<td>{props.obj.name}</td>
<td>{props.obj.birthday}</td>
<td>{props.obj.address}</td>
<td>{props.obj.zipcode}</td>
<td>{props.obj.city}</td>
<td>{props.obj.phone}</td>
<td>{props.obj.email}</td>
<td>
<Link
to={"/edit/" + props.obj.id}
className="waves-effect waves-light btn"
>
Edit
</Link>
</td>
<td>
<button onClick={removeData} className="waves-effect red btn ">
Remove
</button>
</td>
</tr>
</React.Fragment>
);
};
export default Table;
The [] in the useEffect hook is a dependency array to trigger the effect to run. If you want to trigger the effect (without it going off mercilessly), you can create a new variable that triggers that effect to run.
import React, { useState, useEffect } from "react";
import axios from "axios";
import Table from "../Table/Table";
import "./Display.css";
const Display = () => {
const [state, setState] = useState({ students: [], count: "" });
const [requestData, setRequestData] = useState(new Date());
const [searchItem, setsearchItem] = useState({
item: ""
});
const Search = e => {
setsearchItem({ item: e.target.value });
};
useEffect(() => {
axios
.get("/students")
.then(response => {
setState({
students: response.data.students,
count: response.data.count
});
})
.catch(function(error) {
console.log(error);
});
}, [requestData]);
const nameFilter = state.students.filter(list => {
return list.name.toLowerCase().includes(searchItem.item.toLowerCase());
});
return (
<div>
<h3 align="center">Student tables</h3>
<p align="center">Total students: {state.count}</p>
<div className="input-body">
<div className="row">
<div className="input-field col s6">
<input placeholder="search student" onChange={Search} />
</div>
</div>
</div>
<table className="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Date of birth</th>
<th>Address</th>
<th>Zipcode</th>
<th>City</th>
<th>Phone</th>
<th>Email</th>
<th colSpan="2">Action</th>
</tr>
</thead>
{nameFilter.map((object, index) => {
return (
<tbody key={index}>
<Table obj={object} setRequestData={setRequestData} />
</tbody>
);
})}
</table>
</div>
);
};
export default Display;
Then you can trigger it from your Table component:
import React, { useState } from "react";
import { Link } from "react-router-dom";
import axios from "axios";
const Table = props => {
const removeData = () => {
axios
.delete("/students/" + props.obj.id)
.then(() => {
props.setRequestData(new Date());
})
.catch(err => console.log(err));
};
return (
<React.Fragment>
<tr>
<td>{props.obj.name}</td>
<td>{props.obj.birthday}</td>
<td>{props.obj.address}</td>
<td>{props.obj.zipcode}</td>
<td>{props.obj.city}</td>
<td>{props.obj.phone}</td>
<td>{props.obj.email}</td>
<td>
<Link
to={"/edit/" + props.obj.id}
className="waves-effect waves-light btn"
>
Edit
</Link>
</td>
<td>
<button onClick={removeData} className="waves-effect red btn ">
Remove
</button>
</td>
</tr>
</React.Fragment>
);
};
export default Table;
Not sure if helps but you can always remove the item from the current array, so a refresh is not needed, for example you can pass as props a function that receives an id and then filter the students array to exclude the element that matches with that id and then update the state with the new array and count properties, something like this
In your parent:
const Display = () => {
const [state, setState] = useState({ students: [], count: "" });
const deleteItem = (id) => {
const newStudents = state.students.filter(student => student.id !== id)
const newCount = newStudents.length;
setState({ students: newStudents, count: newCount })
}
// Rest of the code
}
Now pass that function to your child component.
<Table obj={object} deleteItem={deleteItem} />
In the child component just modify your removeData method to add the deleteItem prop:
const Table = props => {
const removeData = () => {
axios
.delete("/students/" + props.obj.id)
.then(console.log("Deleted"))
.catch(err => console.log(err));
// Now if your request succeeds call the function to remove the item from the students state array
props.deleteItem(props.obj.id);
};
// Rest of the code
}
I know this does not answer your question, but when you're working with react or it is better to do this computations and filters on the app side, like in this case that even though the record was deleted from the db we also removed the record from the student state object and there's no need to refresh the page.
Remember, you're creating a single page application, so we want the nicest experience for the user without refreshing the page for every action the user makes.
Have a look at this
import React, { useState } from "react";
const Display = () => {
const [refresh, setRefresh] = useState(false)
const delete=() => {
// ................. //delete logic
reload ? setRefresh(false) : setRefresh(true) //toggle just to change state
}
useEffect(() => {
}, [reload]); //inject as dependency
}
I've been following one of the MERN stack tutorials online (making a simple todo app), and decided to go off-script a little bit. I wanted to add a button to delete a specific item. The delete function is working fine, however it requires the user to manually refresh the page after they click the delete button in order to see the new list of elements in my database (MongoDB). I'd like the page to automatically refresh after the click event, however I'm not sure where to start. Within the react render there is a table, which references a variable to actually assemble the components of the table - this is where the delete button exists. Here is my code:
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import axios from 'axios';
const Todo = props => (
<tr>
<td className={props.todo.todo_completed ? 'completed' : ''}>{props.todo.todo_title}</td>
<td className={props.todo.todo_completed ? 'completed' : ''}>{props.todo.todo_description}</td>
<td className={props.todo.todo_completed ? 'completed' : ''}>{props.todo.todo_responsible}</td>
<td className={props.todo.todo_completed ? 'completed' : ''}>{props.todo.todo_priority}</td>
<td>
<Link to={"/edit/"+props.todo._id}>Edit</Link>
</td>
<td>
{/* this is where the delete happens */}
<button onClick={ () =>
axios.delete('http://localhost:4000/todos/'+props.todo._id)
.then(console.log("Deleted: " + props.todo._id))
.catch(err => console.log(err))
}
>Delete</button>
</td>
</tr>
)
export default class TodosList extends Component {
constructor(props) {
super(props);
this.state = {todos: []};
}
componentDidMount() {
axios.get('http://localhost:4000/todos/')
.then(res => {
this.setState({ todos: res.data });
})
.catch(function(err){
console.log(err);
})
}
todoList() {
return this.state.todos.map(function(currentTodo, i){
return <Todo todo={currentTodo} key={i} />;
})
}
render() {
return (
<div>
<h3>Todos List</h3>
<table className="table table-striped" style={{ marginTop: 20 }} >
<thead>
<tr>
<th>Title</th>
<th>Description</th>
<th>Responsible</th>
<th>Priority</th>
<th>Action</th>
<th>Remove Item</th>
</tr>
</thead>
<tbody>
{ this.todoList() }
</tbody>
</table>
</div>
)
}
}
Hopefully someone on here can get me pointed in the right direction.
Thanks
You can delete the specific item from TodosList component state after you have successfully deleted the item from Todo component. For that you can
1) add a method in TodosList component.
deleteItemHandler = (id) => {
const updatedTodos = this.state.todos.filter(todo => todo.id !== id);
this.setState({todos: updatedTodos})
}
2) pass the method deleteItemHandler as props to Todo component
todoList() {
return this.state.todos.map((currentTodo, i) => {
return <Todo todo={currentTodo} deleteItem={this.deleteItemHandler} key={i} />;
})
}
3) use it after item is successfully deleted
<td>
{/* this is where the delete happens */}
<button onClick={ () =>
axios.delete('http://localhost:4000/todos/'+props.todo._id)
.then(() => props.deleteItem(props.todo._id))
.catch(err => console.log(err))
}
>Delete</button>
</td>
Another way
Instead deleting item from TodosList component you can also update the state. For that you can
1) add method that updates in TodosList component
updateStateHandler = () => {
axios.get('http://localhost:4000/todos/')
.then(res => {
this.setState({ todos: res.data });
})
.catch(function(err){
console.log(err);
})
}
2) pass the method updateStateHandler as props to Todo component
todoList() {
return this.state.todos.map((currentTodo, i) => {
return <Todo todo={currentTodo} updateState={this.updateStateHandler} key={i} />;
})
}
3) use it after item is successfully deleted
<td>
{/* this is where the delete happens */}
<button onClick={ () =>
axios.delete('http://localhost:4000/todos/'+props.todo._id)
.then(() => props.updateState())
.catch(err => console.log(err))
}
>Delete</button>
</td>
You need to do this
export default class TodosList extends Component {
constructor(props) {
super(props);
this.state = {todos: []};
this.fetchTodos = this.fetchTodos.bind(this);
}
fetchTodos() {
axios.get('http://localhost:4000/todos/')
.then(res => {
this.setState({ todos: res.data });
})
.catch(function(err){
console.log(err);
});
}
componentDidMount() {
this.fetchTodos();
}
todoList() {
return this.state.todos.map((currentTodo, i) => {
return <Todo todo={currentTodo} fetchTodos={this.fetchTodos} key={i} />;
})
}
...
Todo:
<td>
{/* this is where the delete happens */}
<button onClick={ () =>
axios.delete('http://localhost:4000/todos/'+props.todo._id)
.then(() => {
console.log("Deleted: " + props.todo._id);
props.fetchTodos();
})
.catch(err => console.log(err));
}
>Delete</button>
</td>
The authority that renders the table is your TodosList class, so it needs to be told to do the deleting:
class TodosList extends ... {
...
todoList() {
return this.state.todos.map((currentTodo, i) => {
let onDelete = () => {
this.removeItem(i);
};
// NEVER use an array position as key. The key is meant to uniquely
// identify the _item itself_ and is used in DOM diffing. Moving elements
// inside an array does not change those elements in the slightest and only
// requires moving DOM nodes around, but if you use array position as key,
// what you've now done is said that _everything in the DOM node has changed_
// So: don't do that. Use a real, item based, value.
return <Todo todo={currentTodo} key={currentTodo.id} onDelete={onDelete}/>;
// Of course this assumes todo items have an `id` property.
// If they don't, pick another property _on the todo item_ that
// uniquely identifies it.
});
}
removeItem(i) {
let todos = this.state.todos;
todos.splice(i,1);
// This single call now results in all the UI updates that you
// need to have happen: the todo item is no longer in the state,
// and so its DOM node will be removed from the page. And because
// we're now using real keys, React will not touch any of the other
// DOM nodes. The UI update is near-instant.
this.setState({ todos });
}
...
}
Then the individual buttons can call their own onDelete once deleting has happened:
const deleteThisItem = () => {
axios
.delete('http://localhost:4000/todos/'+props.todo._id)
.then(this.props.onDelete())
.catch(err => console.log(err))
};
<button onClick={deleteThisItem}>delete</button>
So the flow is:
TodoList knows all the todo items,
TodoList generates UI for each todo item,
item UI includes a button that will call TodoList's item deletion function, which will update the TodoList state
simply by virtue of removing an item from that state and calling setState with that change, React will render what needs to be rendered.
I am new to Gatsbyjs and reactjs and i still don't understand much of how props.and states work.
I am building this simple application that gets a list of customers from an API and a list of tasks for each customer.
I am using Reach/Router to render the components. Everything works as expected as for as to displaying a table with a list of customers and when i click on a customer a new page is rendered which shows a list of task for that said customer.
Now, i am trying to make the table editable. I am starting by trying to simply delete some rows. this is where I am stuck.
edit
I believe that i get the error of Uncaught TypeError: Cannot read property 'data' of null because i am trying to access data (state) which is managed by the fetch.js class. How can I pass the data (state) to the ClientTasks class?
---
I have the following code
index.js
import React from "react"
import { createHistory, LocationProvider } from '#reach/router'
import createHashSource from '../utils/hash-source'
import { ToastContainer } from 'react-toastify';
import "../css/main.css"
import "../css/materialize.css"
import "../css/blackjack.css"
import '../../node_modules/react-toastify/dist/ReactToastify.css';
import { NavBar } from '../components/navBar'
import { Main } from '../components/main'
const isClient = typeof window !== 'undefined' && window;
let source
let history
if (typeof window !== `undefined` ) {
source = createHashSource()
history = createHistory(source)
}
class App extends React.Component {
render() {
return (
<LocationProvider history={history}>
<div className="app" >
<NavBar/>
<Main/>
<ToastContainer position="bottom-right"/>
</div>
</LocationProvider>
)
}
}
export default App
main.js
import React from 'react'
import { Router } from "#reach/router"
import { Home } from '../components/home'
import { Customers } from './customers';
import { ClientTasks } from './clientTasks1';
const Main = () => (
<main className="main container">
<Router className="row">
<Home path='/'/>
<Customers path='customers'/>
<ClientTasks path="customers/tasks/:customerId"/>
</Router>
</main>
)
export { Main }
fetch.js
I am using this file to work as a single class component that helps me fetch data for the tables I am displaying (customers and tasks). It works fine as is. there is probably better ways to do it, but for now this is how i am doing it. Any pointers are welcome.
import React, { Component } from 'react'
const axios = require('axios')
class Fetch extends React.Component {
constructor(props) {
super(props);
this.state = {
data: null,
isLoading: true,
error: null,
};
}
componentDidMount() {
this.setState({ isLoading: true });
axios.get(this.props.url)
.then(result => this.setState({
data: result.data,
isLoading: false
}))
.catch(error => this.setState({
error,
isLoading: false
}));
}
render() {
return this.props.children(this.state);
}
}
export default Fetch
Customers.js
This where i display my customers' table. I have links on each customer and with the help of "reach/router" render the cutomer tasks table.
import React, { Component } from 'react'
import { Link } from "#reach/router"
import Fetch from './fetch'
import { UploadForm } from './upLoadtoS3'
import { AnimatedDiv } from './AnimatedDiv'
const APIURL = `https://SomeAIPURL`
let APIQuery = `customers`
const Customers = () => (
<Fetch url={APIURL + APIQuery}>
{({ data, isLoading, error }) => {
if (!data) {
return (
<div className="progress">
<div className="indeterminate"></div>
</div>)
}
if (error) {
return <p>{error.message}</p>;
}
if (isLoading) {
return (
<div className="progress">
<div className="indeterminate"></div>
</div>)
}
return (
<AnimatedDiv className='col m12 s12'>
<h1> Client List </h1>
<table className='highlight'>
<thead>
<tr>
<th>#</th>
<th>Client ID</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{data.map((customer, i) => (
<tr key={customer.customerid}>
<td>{i + 1}</td>
<td>
<Link to={`tasks/${customer.customerid}`}>{customer.customerid}</Link>
</td>
<td>{customer.enabled}</td>
</tr>
))}
</tbody>
</table>
<UploadForm></UploadForm>
</AnimatedDiv>
);
}
}
</Fetch>
)
export { Customers }
ClientTasks.js
Fetch is called once again and populates the table with data pulled from the API.
I used another file to define the contents of this table. listTasks.js
import React, { Component } from 'react'
import { Link } from "#reach/router"
import Fetch from './fetch'
// import Delete from './delete'
import { AnimatedDiv } from './AnimatedDiv'
import DisplayList from './listTasks'
const APIURL = `https://SomeAIPURL`
const CUSTOMERQUERY = `tasks?customerid=`
const TASKQUERY = `&taskid=`
class ClientTasks extends React.Component {
handleDelete(taskToBeDeleted) {
// console.log(taskToBeDeleted);
let newData = this.state.data.filter((_data) => {
return _data != taskToBeDeleted
});
this.setState({ data: newData })
}
render() {
let customerId = this.props.customerId
return (
<Fetch url={APIURL + CUSTOMERQUERY + customerId}>
{({ data, isLoading, error }) => {
if (!data) {
return (
<div className="progress">
<div className="indeterminate"></div>
</div>)
}
if (error) {
return <p>{error.message}</p>;
}
if (isLoading) {
return (
<div className="progress">
<div className="indeterminate"></div>
</div>)
}
else {
return (
<AnimatedDiv className='col m12 s12'>
<h1>{customerId} Tasks</h1>
<table id="customerList" className="highlight" >
<thead>
<tr>
<th>Task ID</th>
<th>Qty</th>
<th>Asset Category</th>
<th>Asset</th>
<th>Location</th>
<th>Status</th>
<th>Action</th>
</tr>
</thead>
<DisplayList handleDelete={this.handleDelete.bind(this)} data={data}/>
</table>
<Link to='/customers'> Back to Client List ... </Link>
</AnimatedDiv>
)
}
}
}
</Fetch>
)
}
}
export { ClientTasks }
>
Here i have an onClick function that runs handleDelete inside the ClientTasks.js file.
If i console.log(taskstobedeleted) then the console shows me the contents of the row that i am trying to delte. This is as for as i get. Then i am trying to use the following function in clientasks.js but i get an error in the console the says Cannot read property 'data' of null) and i believe that is because the props of data are the accessible that this clientTasks class.
I am still learning and there are better ways to structure the code, but i have hit a wall and i don't want to rebuild the app from scratch, if i did i would probably use redux, but that is another lesson for later. I figured that this project of mine is small enough and does not need Redux yet.
Can somehow give me any pointers of how to delete the rows of my table?
handleDelete(taskToBeDeleted) {
// console.log(taskToBeDeleted);
let newData = this.state.data.filter((_data) => {
return _data != taskToBeDeleted
});
this.setState({ data: newData })
}
Listtasks.js
import React from 'react'
import { Icon } from 'react-icons-kit'
import { ic_delete_forever } from 'react-icons-kit/md/ic_delete_forever'
export default class DisplayList extends React.Component {
render() {
return (
<tbody>
{this.props.data.map((task) => (
<tr key={task.taskid}>
<td>{task.taskid}</td>
<td>{task.qty}</td>
<td>{task.category}</td>
<td>{task.asset}</td>
<td>{task.location}</td>
<td>{task.enabled}</td>
<td>
<button style={{ padding: '0px', background: 'transparent', border: '0', cursor: 'pointer' }} onClick={this.props.handleDelete.bind(this, task)} >
<Icon style={{ color: 'red' }} icon={ic_delete_forever} />
</button>
</td>
</tr>
))}
</tbody>
)
}
}
I think your function should be like this:
handleDelete(taskToBeDeleted) {
// console.log(taskToBeDeleted);
let newData = this.state.data.filter((_data) => _data.taskid != taskToBeDeleted.taskid});
this.setState({ data: newData })
}
If your console is giving you object that you wanted, then, first, you do not need return in arrow function since return is implicit. Second, all of your tasks have been returned because you were asking for two objects are they the same which will always be false even if they have the same key value pairs inside of them. They have different references. That is why I used id since I suppose the value of that key is number and you can evaluate that with operator == or !=
I'd like to refresh my Table component when i submit my form.
Code look like this (tell me if the structure is bad.. New in React ;) )
I don't really know how to tell to my DataProvider
"Refresh your data, call the api again when the form is Submit !
App.JS
const App = () => (
<React.Fragment>
<DataProvider endpoint="api/lead/" render={data => <Table data={data} />} />
<Form endpoint="api/lead/" />
</React.Fragment>
);
DataProvider.JS
class DataProvider extends Component {
static propTypes = {
endpoint: PropTypes.string.isRequired,
render: PropTypes.func.isRequired
};
state = {
data: [],
loaded: false,
placeholder: "Loading..."
};
componentDidMount() {
fetch(this.props.endpoint)
.then(response => {
if (response.status !== 200) {
return this.setState({ placeholder: "Something went wrong" });
}
return response.json();
})
.then(data => this.setState({ data: data, loaded: true }));
}
render() {
const { data, loaded, placeholder } = this.state;
return loaded ? this.props.render(data) : <p>{placeholder}</p>;
}
}
export default DataProvider;
Table.JS
const Table = ({ data }) =>
!data.length ? (
<p>Nothing to show</p>
) : (
<div className="column">
<h2 className="subtitle">
Showing <strong>{data.length} items</strong>
</h2>
<table className="table is-striped">
<thead>
<tr>
{Object.entries(data[0]).map(el => <th key={key(el)}>{el[0]}</th>)}
</tr>
</thead>
<tbody>
{data.map(el => (
<tr key={el.id}>
{Object.entries(el).map(el => <td key={key(el)}>{el[1]}</td>)}
</tr>
))}
</tbody>
</table>
</div>
);
Table.propTypes = {
data: PropTypes.array.isRequired
};
export default Table;
And at the end, there is my Form :
handleSubmit = e => {
e.preventDefault();
const { name, email, message } = this.state;
const lead = { name, email, message };
const conf = {
method: "post",
body: JSON.stringify(lead),
headers: new Headers({ "Content-Type": "application/json" })
};
fetch(this.props.endpoint, conf).then(response => console.log(response));
};
thanks