I have a simple application with two react components:
vacancies.jsx - lists vacancies
counter.jsx - shows the number of vacancies from vacancies.jsx
When the application loads the counter shows the correct number of vacancies, but as I start adding/deleting vacancies in vacancies.jsx the count stays the same.
vacancies.jsx:
export class Vacancy extends React.Component {
constructor(props) {
super(props);
this.state = { vacancies: [], loading: true, title:"" };
fetch('/api/Vacancy/Vacancies')
.then(response => response.json())
.then(data => {
this.setState({ vacancies: data, loading: false });
});
}
delete(id) {
var vacancies = this.state.vacancies;
this.setState(
{
vacancies: this.state.vacancies.filter((v) => {
return (v.id != id);
})
});
}
loadVacancies(vacancies) {
return (
<table className='table table-striped'>
<thead>
<tr>
<th>Title</th>
<th>Min Salary</th>
<th>Max Salary</th>
</tr>
</thead>
<tbody>
{vacancies.map(v =>
<tr key={v.id}>
<td>{v.title}</td>
<td>{v.currency} {v.minSalary}</td>
<td>{v.currency} {v.maxSalary}</td>
<td>
<a href="#" onClick={(id) => this.delete(v.id)}>Delete</a>
</td>
</tr>
)}
</tbody>
</table>
);
}
render() {
let contents = this.state.loading
? <p><em>Loading...</em></p>
: this.loadVacancies(this.state.vacancies);
return (
<div>
{contents}
</div>
);
}
}
const containerElement = document.getElementById('content');
ReactDOM.render(<Vacancy />, containerElement);
counter.jsx
import { Vacancy } from "./vacancies";
export class Counter extends Vacancy {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>Items:{this.state.vacancies.length}</h1>
</div>
);
}
}
ReactDOM.render(<Counter />, document.getElementById('counter'));
UI:
According to the react team, they recommend using composition instead of inheritance:
React has a powerful composition model, and we recommend using composition instead of inheritance to reuse code between components.
And
At Facebook, we use React in thousands of components, and we haven’t found any use cases where we would recommend creating component inheritance hierarchies.
https://reactjs.org/docs/composition-vs-inheritance.html
So based on your code, recreate your Counter component like this:
export const Counter = props => {
return (
<div>Items: {props.items}</div>
);
};
Then just change your Vacancies component including your new Counter component there:
loadVacancies(vacancies) {
return (
<Counter items={this.state.vacancies.length} />
<table className='table table-striped'>
<thead>
<tr>
<th>Title</th>
<th>Min Salary</th>
<th>Max Salary</th>
</tr>
</thead>
<tbody>
{vacancies.map(v =>
<tr key={v.id}>
<td>{v.title}</td>
<td>{v.currency} {v.minSalary}</td>
<td>{v.currency} {v.maxSalary}</td>
<td>
<a href="#" onClick={(id) => this.delete(v.id)}>Delete</a>
</td>
</tr>
)}
</tbody>
</table>
);
}
Related
So I have a table I made in a react app, it's currently just rows and columns. I want to add a basic search feature, where a user can type a name and get rows matching that name. I've looked at some examples online, but nothing covers how to add a search feature with the type of table I made. Any tips or knowledge of how to do this given the code I have.
import React from "react";
import './App.css';
class App extends React.Component {
// Constructor
constructor(props) {
super(props);
this.state = {
items: [],
DataisLoaded: false
};
}
// ComponentDidMount is used to
// execute the code
componentDidMount() {
fetch(
"http://ec2-34-213-215-13.us-west-2.compute.amazonaws.com:3001/getPatients")
.then((res) => res.json())
.then((json) => {
this.setState({
items: json,
DataisLoaded: true
});
})
}
render() {
const { DataisLoaded, items } = this.state;
if (!DataisLoaded) return <div>
<h1> Please wait some time.... </h1> </div> ;
return (
<div className = "App">
<h1> Welcome to the Master Patient Index </h1> {
<table class="center">
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>DOB</th>
<th>Gender</th>
<th>SSN</th>
<th>Race</th>
<th>Ethnicity</th>
<th>Marital</th>
<th>Drivers License</th>
<th>Passport</th>
<th>Address</th>
<th>City</th>
<th>State</th>
<th>County</th>
<th>Zip</th>
</tr>
{items.map((items, key) => {
return (
<tr key={key}>
<td>{items.FIRST}</td>
<td>{items.LAST}</td>
<td>{items.BIRTHDATE}</td>
<td>{items.GENDER}</td>
<td>{items.SSN}</td>
<td>{items.RACE}</td>
<td>{items.ETHNICITY}</td>
<td>{items.MARITAL}</td>
<td>{items.DRIVERS}</td>
<td>{items.PASSPORT}</td>
<td>{items.ADDRESS}</td>
<td>{items.CITY}</td>
<td>{items.STATE}</td>
<td>{items.COUNTY}</td>
<td>{items.ZIP}</td>
</tr>
)
})}
</table>
}
</div>
);
}
}
export default App;
I would do this by adding a new property to your state called searchTerm.
constructor(props) {
super(props);
this.state = {
items: [],
searchTerm: ''
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({
[event.target.name]: event.target.value
})
}
Then add an input field to update the searchTerm
<input placeholder="search here..." value={searchTerm} name="searchTerm" onChange={this.handleChange} />
Then using the .filter array method to filter by search term
{items
.filter(items => items.FIRST.toLowerCase().includes(searchTerm.toLowerCase()))
.map((items, key) => {
return (
<tr key={key}>
<td>{items.FIRST}</td>
</tr>
)
})}
Full code: (simplied version of your code)
https://codesandbox.io/s/cool-bird-i2nj5o?file=/src/App.js:844-1183
Still following the following tutorial:
https://www.techiediaries.com/php-react-rest-api-crud-tutorial/
I am finally getting no errors in the console. But I am getting nothing but a blank array in the console that simply reads [ ]
The complete code is as follows:
<script type="text/babel">
class App extends React.Component {
componentDidMount() {
const url = 'api/testQuery.php'
axios.get(url).then(response => response.data)
.then((data) => {
this.setState({ users: data })
console.log(this.info.users)
})
}
info = {
users: []
}
render() {
return (
<React.Fragment>
<h1>All Users</h1>
<table border='1' width='100%'>
<tr>
<th>Username</th>
<th>Fullname</th>
<th>Email</th>
</tr>
{this.info.users.map((user) => (
<tr>
<td>{ user.username }</td>
<td>{ user.fullname }</td>
<td>{ user.email }</td>
</tr>
))}
</table>
</React.Fragment>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
</script>
The json that is being returned from the testQuery.php script looks like this:
[
{
"uid":"7",
"username":"tna.mjordan",
"fullname":"Michael Jordan",
"email":"tna.mjordan#tna.com",
"userlevel":"9",
"division":"Chicago"
},
// and so on
]
Any thoughts as to why I am getting a blank users array, and how to fix it?
I think you need to print the info from the state, not the attribute itself. Same happens in the returned renderized HTML. Besides, the setState should modify the info attribute, not the users property within. Below you can find your code with the changes applied:
class App extends React.Component {
componentDidMount() {
const url = "api/testQuery.php";
axios
.get(url)
.then((response) => data)
.then((data) => {
this.setState({ info: { users: data } });
console.log(this.state.info.users);
});
}
info = {
users: [],
};
render() {
return (
<React.Fragment>
<h1>All Users</h1>
<table border="1" width="100%">
<tr>
<th>Username</th>
<th>Fullname</th>
<th>Email</th>
</tr>
{this.state.info.users.map((user) => (
<tr>
<td>{user.username}</td>
<td>{user.fullname}</td>
<td>{user.email}</td>
</tr>
))}
</table>
</React.Fragment>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
I am having problem with sorting table column in React. My table composes of three components: one defining the individual row (row.js), one rendering headers and mapping the rows that need own states (rows.js) and finally table.js that renders the whole thing. The data of the table comes from the database.
Here is a part of the row.js:
class ProjectTableProjectRow extends Component {
render() {
const { project } = this.props;
return (
<tr>
<td className="projects">
<Body2>
<Link to={`/projects/${project.id}`}>{project.description}</Link>
</Body2>
</td>
export default withRouter(ProjectTableProjectRow);
And here an excerpt from my rows.js:
class ProjectTableProjectRows extends Component {
componentDidMount() {
this.props.projects.getAll(); // This gets all the projects from the store
}
onSortProjects = () => {
let sortedToBe = this.props.projects.list.map(project => project.description);
const sorted = sortedToBe.sort();
};
render() {
return (
<table>
<thead>
<tr>
<th>
<Caption>Project</Caption>
<IconButton onClick={() => this.onSortProjects()}>
<RowsIcon />
</IconButton>
</th>
</tr>
</thead>
{this.props.projects.list.map(project => (
<tbody key={project.id}>
<ProjectTableProjectRow project={project} />
</tbody>
))}
</table>
);
}
}
export default ProjectTableProjectRows;
Finally, there is a projectTable.js (that I am not sure if I need anyway...)
class ProjectTable extends Component {
render() {
return (
<>
<ProjectTableProjectRows projects={this.props.projects} />
</>
);
}
}
export default ProjectTable;
So, I'd like to sort the project.description column (there are project names as strings) in alphabetical order. Naturally the icon and sort function onClick in it is not connected to the table column, so the sort function does nothing to the table. How can I achieve this? I do not know yet how to think "in React".
This is obviously not tested, I'm just making a couple of updates to the code you had but this will allow you to use the sorted values.
class ProjectTableProjectRows extends Component {
constructor(props) {
super(props)
this.state = {
projects: null
}
}
componentDidMount() {
const projects = this.props.projects.getAll();
this.setState({ projects })
}
onSortProjects = () => {
let sortedToBe = this.state.projects.list.map(project => project.description);
const sorted = sortedToBe.sort();
this.setState({ projects: sorted });
};
render() {
return (
<table>
<thead>
<tr>
<th>
<Caption>Project</Caption>
<IconButton onClick={() => this.onSortProjects()}>
<RowsIcon />
</IconButton>
</th>
</tr>
</thead>
{this.state.projects && this.state.projects.list.map(project => (
<tbody key={project.id}>
<ProjectTableProjectRow project={project} />
</tbody>
))}
</table>
);
}
}
export default ProjectTableProjectRows;
I can get all props passed in React class in its render method.
But if I try to do the same in componentDidMount() or even in constructor() - no luck.
When debugging I can see props in
componentDidMount(){
const { users } = this.props <-props are already there
console.log('users from did mount', users); <-- undefined here
}
and nothing appears in users variable.
When the same code fires in render() method -- all is working fine.
upd: full class
import React, { Component } from 'react'
import { firebaseDB, functions } from '../firebase'
import map from 'lodash/map'
export default class Users extends Component {
state = {
users: []
}
componentDidMount(){
const { users } = this.props
console.log('users from did mount', users); // undefined here
// here I'd like to some work with those props
}
handleUser = user => () => {
// console.log('user to handle:',user); //is here
... handling user data
}
render() {
const { users } = this.props //ok
console.log('users from render', users);//see it
return (
<div className="container">
{users ?
<div>
<h4 className="text-primary">Users List</h4>
<table className="table">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">email</th>
<th scope="col">Something</th>
</tr>
</thead>
<tbody>
{
map(users, (user, key) => (
<tr key={key}>
<td>{user.displayName}</td>
<td>{user.email}</td>
<td><button
className="btn btn-primary"
onClick={this.handleUser(user)}
>ok</button></td>
</tr>
))
}
</tbody>
</table>
</div>
:
"There are no users yet "
}
</div>
)
}
}
users are firebase object, comes from parent above
{ id1: {...}, id2: {...} }
You need to use componentWillReceiveProps method in case if users prop is coming from async
componentWillReceiveProps(nextProps){
if(this.props.users !== nextProps.users){
console.log('should appear here', nextProps.users);
}
}
I get data from github api
I have all the data i need to display, but I want to splice it so that i only get 20 repositories per page.
And I don't want a framework or a plugin for that.
I'm fairly new to React and JS in general so I don't know where to start or what to do next to create a pagination.
import React, {Component} from 'react';
import axios from 'axios';
class Apirequest extends Component {
constructor(){
super();
this.state = {
githubData: [],
};
}
componentDidMount() {
axios.get('https://api.github.com/search/repositories?q=language:javascript&sort=stars&order=desc&per_page=100')
.then(res => {
console.log('res', res)
this.setState({ githubData: res.data.items})
})
}
render() {
const { githubData } = this.state
return(
<div className="container">
{githubData.map((name, index) =>
<table key={name.id}>
<tr>
<th><img src={name.owner.avatar_url}/></th>
<td>{name.owner.login}<div className="border-bottom"></div></td>
<td>{name.description}<div className="border-bottom"></div></td>
<td><a href={name.homepage}>{name.homepage}</a></td>
</tr>
</table>
)}
</div>
)
}
}
export default Apirequest;
First of all your map function has a wrong logic. You are creating a table for each record and you should only create a row for each record. table tags should be outside of map.
render() {
const { githubData } = this.state
return(
<div className="container">
<table key={name.id}>
{githubData.map((name, index) =>
<tr>
<th><img src={name.owner.avatar_url}/></th>
<td>{name.owner.login}<div className="border-bottom"></div></td>
<td>{name.description}<div className="border-bottom"></div></td>
<td><a href={name.homepage}>{name.homepage}</a></td>
</tr>
)}
</table>
</div>
)
}
For pagination what you can do is to limit the number of rows you show by using Array.prototype.slice(). Just to give you an idea I am posting a small example. You might need to implement some more for this logic to work on your code.
Example
previousPage = () => {
if (this.state.currentPage !== 1)
this.setState((prevState) => ({currentPage: (prevState.currentPage - 1)}))
}
nextPage = () => {
if (this.state.currentPage + 1 < this.state.githubData.lenght)
this.setState((prevState) => ({currentPage: (prevState.currentPage + 1)}))
}
render() {
const { githubData, currentPage } = this.state
return(
<div className="container">
<table key={name.id}>
{githubData.slice((currentPage * 20), 20).map((name, index) =>
<tr>
<th><img src={name.owner.avatar_url}/></th>
<td>{name.owner.login}<div className="border-bottom"></div></td>
<td>{name.description}<div className="border-bottom"></div></td>
<td><a href={name.homepage}>{name.homepage}</a></td>
</tr>
)}
</table>
<button onClick={this.previousPage}>Previous Page</button>
<button onClick={this.nextPage}>Next Page</button>
</div>
)
}
set your state to have pagination info and data.
such as
state = {
pagination: {
start: 0,
rows: 20
},
githubData: ....
}
and now in you render function you can splice based on the pagination info. Anytime new page is clicked, you can set state to new start variable