I'm trying to pass method ref to functional component but somehow it doesn't work
Here is the function:
import { FaTrashAlt } from 'react-icons/fa';
const ArticlesData = props => {
return(
props.products.map(product => {
return (
<tr>
<td>{product.name}</td>
<td>{product.description}</td>
<td>{product.price}$</td>
<td>
<span className="removeProduct--Container" onClick={props.click}>
<FaTrashAlt className="remove--Icon" />
</span>
</td>
</tr>
)
}).reverse()
)
}
export default ArticlesData;
Here is the request I'm trying to pass:
onRemove = (id) => {
fetch(`http://localhost:5000/products/:${id}/delete`, {
method: 'POST'
})
}
And here is how I pass:
<ArticlesData products={this.state.products} click={this.onRemove}/>
Update:
controller:
router.post('/:id/delete', (req, res) => {
try {
console.log(req.params.id)
productService.deleteOne(req.params.id)
res.status(200)
} catch (error) {
console.log(error)
}
})
service:
function deleteOne(id) {
return Product.deleteOne({_id: id});
}
You need to call the function with parameter id.
I'm assuming your product object has id attribute:
import { FaTrashAlt } from 'react-icons/fa';
const ArticlesData = props => {
return(
props.products.map(product => {
return (
<tr>
<td>{product.name}</td>
<td>{product.description}</td>
<td>{product.price}$</td>
<td>
<span className="removeProduct--Container" onClick={() => props.click(product.id)}>
<FaTrashAlt className="remove--Icon" />
</span>
</td>
</tr>
)
}).reverse()
)
}
export default ArticlesData;
Change ArticlesData component's code
from onClick={props.click}
to onClick={() => props.click(product.id)}
Full code:
import React from "react";
import ArticlesData from "./ArticlesData";
export default class SomeName extends React.Component {
onRemove = (id) => {
console.log(id);
fetch(`http://localhost:5000/products/:${id}/delete`, {
method: 'POST'
})
};
render() {
return (
<>
<ArticlesData click={this.onRemove} />
</>
);
}
}
import { FaTrashAlt } from 'react-icons/fa';
const ArticlesData = props => {
return(
props.products.map(product => {
return (
<tr>
<td>{product.name}</td>
<td>{product.description}</td>
<td>{product.price}$</td>
<td>
<span className="removeProduct--Container" onClick={() => props.click(product.id)}>
<FaTrashAlt className="remove--Icon" />
</span>
</td>
</tr>
)
}).reverse()
)
}
export default ArticlesData;
CodeSandbox Demo
Related
I create a table I get data from the database using the backend I just want to show the output of the table on the page the output will not be visible
This is the code of my table.js
//import Data from './TextForm';
function Table(props) {
console.log('type ', typeof (props.Data));
console.log('data ', props.Data)
return (
<table>
<thead>
<tr>
<th>Text No</th>
<th>TextArea</th>
</tr>
</thead>
<tbody>
{props.Data ?
Object.entries(props.Data).map((key,value)=> {
console.log('Key',key);
{
<tr key={value}>
<td>{key.textId}</td>
<td>{key.textArea}</td>
</tr>
}
})
: null
}
</tbody>
</table>
)
}
export default Table;
this is props. data where I get the data and define prop. data I get data from the backend I connected the frontend for getting and storing data
Edit
function TextForm(props) {
const [text, setText] = useState('');
const [submittext,setsubmittext]=useState(null);
const [Data,setData]=useState([]);
const handleOnClickUpperCase = () => {
var newText = text.toUpperCase();
setText(newText);
}
const handleOnClickLowerCase = () => {
var newText = text.toLowerCase();
setText(newText);
}
const handleOnChange = (e) => {
setText(e.target.value);
}
const handleOnPreview = (e) => {
e.preventDefault();
setsubmittext(text);
// console.log(text);
const ROOT_URL='https://localhost:7113/';
var formData={
Textarea:text
}
axios.post(`${ROOT_URL}api/demo-text`, formData, {
headers: {"Access-Control-Allow-Origin": "*", 'Content-Type': 'application/json'}
})
.then(function (response) {
console.log('successs')
//handle success
setData(response);
console.log('response ',Data);
})
.catch(function (response) {
console.log('error')
//handle error
console.log(response);
})
}
return (
<>
<div className="container">
<h1>{props.title}</h1>
<p>Enter Text Here:</p>
<div className="mb-3">
<textarea className="form-control" value={text} onChange={handleOnChange} id="mybox" rows="8"></textarea>
</div>
<Table Data={Data} />
{text === text.toLowerCase() ? <button className="btn btn-primary" onClick={handleOnClickUpperCase}>Convert to Upper Case</button> : <button className="btn btn-primary" onClick={handleOnClickLowerCase}>Convert to Lower Case</button>}
<button className="btn btn-primary mx-3" onClick={handleOnPreview}>submit</button>
</div>
<hr></hr>
<div className="container my-4" >
<h1>{props.sum}</h1>
<p>Text Word {text.split(" ").length} and Character is {text.length}</p>
<p>{0.008 * text.split(" ").length} Minutes to Read</p>
</div>
<hr></hr>
<div className="container my-4">
<h2>Preview Your Text</h2>
<p>{submittext}</p>
</div>
</>
)
}
the output of prop.data
here have iterator objects array so pls try the following the code see I created an example as your same array object as you shared img pls find an example here link.
import React from "react";
import ReactDOM from "react-dom";
const Data = {
data: {
result: [
{ textId: 1, textarea: "test" },
{ textId: 2, textarea: "1234" },
{ textId: 3, textarea: null },
{ textId: 4, textarea: null },
]
}
};
function Table(props) {
console.log("type ", typeof props.Data);
console.log("data ", props.Data);
return (
<table>
<thead>
<tr>
<th>Text No</th>
<th>TextArea</th>
</tr>
</thead>
<tbody>
{props?.Data?.data?.result?.map((item) => (
<>
<tr key={item.textId}>
<td>{item.textId}</td>
<td>{item.textarea}</td>
</tr>
</>
))}
</tbody>
</table>
);
}
function App() {
return (
<>
<Table Data={Data} />
</>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
So you have to iterate over props.Data.data.result instead of props.Data, You don't need the Object.entries(),
and make the initial value of the Data = {} instead of [].
const [Data, setData] = useState({});
In the conditional JSX code, you need to check if the object has keys or not, and you don't need Object.entries method, just go with props.Data.data.result.map(), Also you have to return tr from the map method as #Usama mentioned.
{Object.keys(props.Data).length ?
props.Data.data.result.map((key,value)=> {
return (<tr key={value}>
<td>{key.textId}</td>
<td>{key.textArea}</td>
</tr>)
})
: null
}
I am starting in the development of applications with apollo, react and graphql, I have created a form to capture brands of vehicle and show them in a table. When you start the page for the first time, it shows the table well, but when I try to record a new brand it throws me the following image. Please I need help.
this is my code
import React, { Component, Fragment } from 'react';
import { Query, Mutation } from 'react-apollo';
import BrandItem from '../models/BrandItem';
import * as BrandService from '../services/BrandService';
export class Brand extends Component {
render() {
let input;
return (
<div className="divform">
<Mutation mutation={BrandService.ADD_BRAND}
update={(cache, { data: { addBrand } }) => {
const { brands } = cache.readQuery({ query: BrandService.BRAND_QUERY });
cache.writeData({
query: BrandService.BRAND_QUERY,
data: { brands: brands.concat([addBrand]) },
});
}}
>
{
(addBrand, { data }) => (
<form onSubmit={e => {
e.preventDefault();
addBrand({ variables: { description: input.value } });
console.log(data);
input.value = '';
}}>
<label htmlFor="description">Description</label>
<input type="text" id="description" name="description" required ref={node => { input = node; }} />
<input type="submit" value="Send" />
</form>
)
}
</Mutation>
<div>
<Fragment>
<Query query={BrandService.BRAND_QUERY}>
{
({ loading, error, data }) => {
if (loading) return <div><h4>loading....</h4></div>
if (error) console.log(error);
return <Fragment>
<table>
<thead>
<tr>
<th>Id</th>
<th>Description</th>
</tr>
</thead>
<tbody>
{
data.brands.map(brand => (
<BrandItem key={brand.id} brand={brand} />
))
}
</tbody>
</table>
</Fragment>
}
}
</Query>
</Fragment>
</div>
</div>
)
}
}
export default Brand
what am I doing wrong?
Thanks
the only place you use map is here.
{
data.brands.map(brand => (
<BrandItem key={brand.id} brand={brand} />
))
}
Therefore, data.brands must be null or undefined.
I would check the query BrandService.BRAND_QUERY and check it has brands defined in the query. It may help if you added a console.log(data) to double check the structure of the response.
If it makes sense that there are no brands returned in some cases. The simplest way to do that would be to do.
{
data.brands && data.brands.map(brand => (
<BrandItem key={brand.id} brand={brand} />
))
}
However the best thing to do depends on your app.
Essentially the app at this point displays a list "Students" in the view saved to the data base—now I'd like to be able to delete a Student and have that persist as well. I believe the answer is in the component itself.
Here is what I have thus far—this is my Students component:
import React, { Component } from "react";
import store from "../store";
import { scrubStudent } from "../reducers";
export default class Students extends Component {
constructor(props) {
super(props);
this.state = store.getState();
this.deleteStudent = this.deleteStudent.bind(this);
}
deleteStudent(itemIndex) {
console.log(this.state);
var students = this.state.students;
store.dispatch(scrubStudent(this.state));
students.splice(itemIndex, 1);
this.setState({
students: students
});
}
render() {
var students = this.props.students;
return (
<div className="container">
<div className="sixteen columns">
<h1 className="remove-bottom">Students</h1>
<h5>List of current students and their campus</h5>
<hr />
</div>
<div className="sixteen columns">
<div className="example">
<div>
<table className="u-full-width">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Email</th>
<th>Campus</th>
</tr>
</thead>
<tbody>
{students.map(function(student, index) {
return (
<tr key={index}>
<td>
{student.id}
</td>
<td>
{student.name}
</td>
<td>
{student.email}
</td>
<td>
{student.campus}
</td>
<td>
<a
className="button button-icon"
onClick={this.deleteStudent(index)}
>
<i className="fa fa-remove" />
</a>
</td>
</tr>
);
})}
</tbody>
</table>
</div>
</div>
</div>
</div>
);
}
}
Right now I get index.js:90 TypeError: Cannot read property 'deleteStudent' of undefined
Thanks in advance!
UPDATE
Based on Matthew's suggestion I sought guidance (I am in school) from a teacher, he helped me wire the following:
But now I am getting the following error:
`index.js:90 TypeError: Cannot read property 'setState' of undefined`
I am going to dig into the reason for that!
import React, { Component } from "react";
import store from "../store";
import { deleteStudent } from "../reducers";
export default class Students extends Component {
constructor(props) {
super(props);
this.state = store.getState();
this.deleteStudent = this.deleteStudent.bind(this);
}
componentDidMount() {
this.unsubscribe = store.subscribe(function() {
this.setState(store.getState());
});
}
componentWillUnmount() {
this.unsubscribe();
}
deleteStudent(index) {
console.log(this.state);
var students = this.state.students;
store.dispatch(deleteStudent(index));
this.state = store.getState();
}
render() {
var students = this.props.students;
return (
<div className="container">
<div className="sixteen columns">
<h1 className="remove-bottom">Students</h1>
<h5>List of current students and their campus</h5>
<hr />
</div>
<div className="sixteen columns">
<div className="example">
<div>
<table className="u-full-width">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Email</th>
<th>Campus</th>
</tr>
</thead>
<tbody>
{students.map(function(student, index) {
return (
<tr key={index}>
<td>
{student.id}
</td>
<td>
{student.name}
</td>
<td>
{student.email}
</td>
<td>
{student.campus}
</td>
<td>
<a
className="button button-icon"
onClick={() => this.deleteStudent(student.id)}
key={index}
>
<i className="fa fa-remove" />
</a>
</td>
</tr>
);
}, this)}
</tbody>
</table>
</div>
</div>
</div>
</div>
);
}
}
This is my reducer:
import { combineReducers } from "redux";
import axios from "axios";
// INITIAL STATE
const initialState = {
students: [],
campuses: []
};
//ACTION CREATORS
const UPDATE_NAME = "UPDATE_NAME";
const ADD_STUDENT = "ADD_STUDENT";
const DELETE_STUDENT = "DELETE_STUDENT";
const GET_STUDENTS = "GET_STUDENTS";
const UPDATE_CAMPUS = "UPDATE_CAMPUS";
const GET_CAMPUS = "GET_CAMPUS";
const GET_CAMPUSES = "GET_CAMPUSES";
// ACTION CREATORS
export function updateName(name) {
const action = {
type: UPDATE_NAME,
name
};
return action;
}
export function addStudent(student) {
return {
type: ADD_STUDENT,
student
};
}
export function scrubStudent(student) {
return {
type: DELETE_STUDENT,
student
};
}
export function getStudents(students) {
const action = {
type: GET_STUDENTS,
students
};
return action;
}
export function updateCampus(campus) {
const action = {
type: UPDATE_CAMPUS,
campus
};
return action;
}
export function getCampus(campus) {
const action = {
type: GET_CAMPUS,
campus
};
return action;
}
export function getCampuses(campuses) {
const action = {
type: GET_CAMPUSES,
campuses
};
return action;
}
//THUNK CREATORS
export function fetchStudents() {
return function thunk(dispatch) {
return axios
.get("/api/students")
.then(function(res) {
return res.data;
})
.then(function(students) {
return dispatch(getStudents(students));
})
.catch(function(err) {
return console.error(err);
});
};
}
export function postStudent(student) {
return function thunk(dispatch) {
return axios
.post("/api/students", student)
.then(function(res) {
return res.data;
})
.then(function(newStudent) {
return dispatch(addStudent(newStudent));
})
.catch(function(err) {
return console.error(err);
});
};
}
export function deleteStudent(student) {
return function thunk(dispatch) {
return axios
.delete("/api/students/" + student.toString())
.then(function(res) {
return res.data;
})
.then(function(student) {
return dispatch(scrubStudent(student));
})
.catch(function(err) {
return console.error(err);
});
};
}
export function fetchCampuses() {
return function thunk(dispatch) {
return axios
.get("/api/campuses")
.then(function(res) {
return res.data;
})
.then(function(campuses) {
return dispatch(getCampuses(campuses));
})
.catch(function(err) {
return console.error(err);
});
};
}
export function postCampus(student) {
return function thunk(dispatch) {
return axios
.post("/api/campuses", campuse)
.then(function(res) {
return res.data;
})
.then(function(newCampus) {
return dispatch(getCampus(newCampus));
})
.catch(function(err) {
return console.error(err);
});
};
}
// REDUCER
const rootReducer = function(state = initialState, action) {
var newState = Object.assign({}, state);
switch (action.type) {
case GET_STUDENTS:
newState.students = state.students.concat(action.students);
return newState;
case ADD_STUDENT:
newState.students = state.students.concat([action.student]);
return newState;
case DELETE_STUDENT:
newState.students = state.students.concat([action.student]);
return newState;
case GET_CAMPUSES:
newState.campuses = state.campuses.concat(action.campuses);
return newState;
case GET_CAMPUS:
newState.campuses = state.campuses.concat([action.campus]);
return newState;
default:
return state;
}
};
export default rootReducer;
You should use arrow function in order to keep the relevant context in the map function:
{students.map((student, index) => {
This way when you use this inside the function - it's your current component.
I'm using this module for my twitch API app: https://github.com/joshwcomeau/react-flip-move/
and currently having an issue with the leave animation. The enter animation works perfectly, fine, but unforunately, when I click 'x' on one of the channels, the element (in my case a ) moves up and to the right. How do I make it fade out in its current position?
import React, { Component } from 'react';
import { connect } from 'react-redux';
import FlipMove from 'react-flip-move';
import { selectUser, fetchUser, removeUser } from '../actions/index';
class UsersList extends Component {
constructor(props) {
super(props);
this.state = {
show: 'all',
};
this.fetchInitialUsers(this.props.initialUsers);
}
fetchInitialUsers(users) {
users.map(this.props.fetchUser);
}
renderUser(user) {
const { channelData, streamData } = user;
return (
<tr
key={channelData.display_name}
onClick={() => this.props.selectUser(user)}
className='list-item'>
<td>
<img src={channelData.logo} className='user-logo' />
</td>
<td>
{channelData.display_name}
</td>
<td>
{streamData.stream ?
<span className='online'>Online</span> :
<span className='offline'>Offline</span>}
</td>
<span
className="glyphicon glyphicon-remove"
onClick={() => this.props.removeUser(user)}></span>
</tr>
)
}
showOnline() {
this.setState({
show: 'online'
});
}
showOffline() {
this.setState({
show: 'offline'
});
}
showAll() {
this.setState({
show: 'all'
});
}
render() {
return (
<div className='col-sm-4'>
<div className='text-center'>
<div className='btn-group btn-group-sm' role='group'>
<button
className='btn btn-default'
onClick={this.showAll.bind(this)}>
All
</button>
<button
className='btn btn-default'
onClick={this.showOnline.bind(this)}>
Online
</button>
<button
className='btn btn-default'
onClick={this.showOffline.bind(this)}>
Offline
</button>
</div>
</div>
<div className='container'>
<table className='table table-hover'>
<thead>
<tr>
<th>Logo</th>
<th>Channel</th>
<th>Status</th>
</tr>
</thead>
{/* <tbody> */}
<FlipMove
typeName='tbody' enterAnimation='fade'
leaveAnimation='fade'>
{this.props.users.filter(user => {
const { show } = this.state;
const { streamData } = user;
if (show == 'online') {
return streamData.stream;
}
else if (show == 'offline') {
return !streamData.stream;
}
else {
return user;
}
}).map(this.renderUser.bind(this))}
</FlipMove>
{/* </tbody> */}
</table>
</div>
</div>
)
}
}
function mapStateToProps({ users, initialUsers }) {
return { users, initialUsers };
}
export default connect(mapStateToProps, { selectUser, fetchUser, removeUser })(UsersList);
just add maintainContainerHeight="true" to the flipmove attributes
I am trying to render some child components in a parent component but nothing is rendering. I'm not getting any console errors but there is no render. I can't figure out why this may be happening. The application I am working on is built with React, using a flux architecture.
Here is my code:
Parent Component:
import React from 'react';
import TableWithDataHeader from './TableWithDataHeader.jsx';
import TableWithDataBody from './TableWithDataBody.jsx';
import TableWithDataRowForm from './TableWithDataRowForm.jsx';
import AppStore from '../../stores/AppStore';
export default class TableWithData extends React.Component {
state = {rows: [], isEditing: false};
componentDidMount() {
let json = AppStore.getCells();
let rows = this.state.rows;
for (let key in json) {
{rows[key] = json[key]};
}
this.setState({rows});
console.log(rows);
}
handleEdit = (row) => {
this.setState({isEditing: true});
};
editStop = (formKey) => {
this.setState({isEditing: false});
};
handleSubmit = () => {
console.log('hello');
};
render() {
let {rows, isEditing} = this.state;
console.log(rows);
return (
<div>
<div className="row">
<table className="table table-striped">
<thead>
<TableWithDataHeader />
</thead>
<tbody>
{rows.map(row => this.state.isEditing ?
<TableWithDataRowForm formKey={row.id} key={row.id} editStop={this.editStop(formKey)} handleSubmit={this.handleSubmit} /> :
<TableWithDataBody key={row.id} value={row.historycells.contents} handleEdit={this.handleEdit(row)} />
)}
</tbody>
</table>
</div>
</div>
);
}
}
RowForm:
import React from 'react';
export default class TableWithDataRowForm extends React.Component {
editStop = () => {
this.props.editStop();
};
handleSubmit = (e) => {
e.preventDefault();
this.props.handleSubmit();
};
render() {
return (
<tr>
<td></td>
<td>
<button className=""><i className="btn btn-default" onClick={this.editStop}></i>Cancel</button>
<button className="btn btn-success"><i className="fa fa-cloud" onClick={this.handleSubmit}></i>Save</button>
</td>
</tr>
);
}
}
Table Head:
import React from 'react';
import AppStore from '../../stores/AppStore';
export default class TableWithDataHeader extends React.Component {
addHeaders() {
let headerArray = AppStore.getTable().columns;
let headerList = headerArray.map((element, index) => {
return (
<th key={index} id={element.id} className="text-center">{element.name}</th>
);
});
return headerList;
}
render() {
return (
<tr>
{this.addHeaders()}
<th></th>
</tr>
);
}
}
Table Body:
import React from 'react';
export default class TableWithDataBody extends React.Component {
handleEdit() {
this.props.handleEdit();
}
render() {
return (
<tr>
{this.props.histroycells.map(cell => {
return <Cell key={cell.id} value={cell.contents} />
})}
<td>
<button className="btn btn-primary" onClick={this.handleEdit}><i className="fa fa-pencil"></i>Edit</button>
</td>
</tr>
);
}
}
The table header renders fine but neither the body of the table or the edit form shows up at all!
Any help would be much appreciated, especially examples!
Thanks for you time!
Maybe this will help:
Inside your <TableWithDataBody>component, you try to access this.props.historycells, but this isn't passed as a prop.
You render your table rows with:
<TableWithDataBody
key={row.id}
value={row.historycells.contents}
handleEdit={this.handleEdit(row)} />
Maybe if you change render to:
<TableWithDataBody
key={row.id}
historycells={row.historycells} // changed this parameter
handleEdit={this.handleEdit(row)} />
UPDATE:
The line which loops over the props should still read:
{this.props.historycells.map(cell => {
PS: please also fix typo in histroycells.
You have a typo in your code. histroycells in TableWithDataBody should be historycells.