PHP, MySQL, React, Rest API tutorial confusion Part 2 - javascript

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"));

Related

How to add basic search feature to react table

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

How to filter through a table with React?

I am trying to filter through an array so that when a certain tab is clicked, only those results show. I have managed to isolate the certain variables I want to remain but the others that don't fit the criteria still remain. How do I get the filter method to actually render on the page so that the results can be shown. I have searched for hours for and have tried to get the tbody and sort through that but I just get confused as I am mostly new to javascript and react. Can someone point me in the right direction?
Filter Method
const tbody = document.getElementsByTagName('tbody')
console.log(tbody)
//change active class
function addTabBackground() {
const tabs = document.querySelectorAll('[data-tab]')
window.onload = function () {
tabs.forEach(tab => {
tab.addEventListener('click', () => {
if (tab.getAttribute('data-tab') === 'gains') {
listOfOptions.map(option => {
console.log(option.totalProfit)
})
}
tabs.forEach(tab => {
tab.classList.remove('active')
})
tab.classList.add('active')
})
})
}
}
<div className="outputs" >
<table>
<thead>
<tr>
<th>Date</th>
<th>Stock Name</th>
<th>Price Of Option</th>
<th>Number Of Options</th>
<th>Total Amount Spent</th>
<th>Option Sold At</th>
<th>Amount Of Options Sold</th>
<th>Proft</th>
</tr>
</thead>
{listOfOptions.map(option => {
return (
<tbody>
<tr>
<td>{option.clock}</td>
<td>{option.name.toUpperCase()}</td>
<td>${option.price}</td>
<td>{option.amountOfOptions}</td>
<td>${option.totalAmountSpent.toFixed(2)}</td>
<td>${option.optionPriceSoldAt}</td>
<td>{option.amountOfOptionsSold}</td>
<td style={{ color: option.totalProfit >= 0 ? 'green' : 'red' }}>${option.totalProfit.toFixed(2)}</td>
</tr>
</tbody>
)
})}
</table>
</div>
I have used React-Bootstrap-v5 to get Nav, Nav.Item with eventKey and then passed selectedKey in its onSelect function to change the tabs.
Then once we get the data inside my data variable, I used the map function to go over the array. Inside Map Function I have used the required condition to filter the elements i.e. variable = 'Open' or 'Live'.
This will only show the Open type in Open Tab and Live Type Data inside Live Tab.
Hope it's clear to you.
import React, { useEffect, useState } from 'react';
const TestSeries = () => {
// Declare Variable for data to be fetched from API
const [data, setData] = useState([]);
const fetchTestData = async () => {
const response = await axios.get(site_ip + '/apiLink');
setData(response.data.Content);
};
useEffect(() => {
fetchTestData();
}, []);
// State Variable to keep track of active tab
const [activeTab, setActiveTab] = useState('Open');
return (
<>
<Nav
activeKey={activeTab}
fill
variant="pills"
onSelect={(selectedKey) => {
setActiveTab(selectedKey);
}}
>
<Nav.Item>
<Nav.Link eventKey="Open">Open</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link eventKey="Live">Live</Nav.Link>
</Nav.Item>
</Nav>
<br />
<Table striped bordered hover>
<thead>
<tr>
<th>#</th>
<th>Column1</th>
<th>Column2</th
<th>Column3</th>
<th>Column4</th>
<th>Data Type</th>
</tr>
</thead>
<tbody>
{data.map((item, index) =>
// Condition by which data will be filtered
item.data_type == activeTab ? (
<tr>
<td>{index + 1}</td>
<td>{item.col1}</td>
<td>{item.col2}</td>
<td>{item.col3} </td>
<td>{item.col4}</td>
<td>{item.data_type}</td>
</tr>
) : null
)}
</tbody>
</Table>
</>
);
};
export default TestSeries;
Result
Assuming from the comment that you have something vaguely looking like:
function Page() {
return (
<>
<Navbar />
<Table />
</>
)
}
What you need to do is to store the current tab in a state, and pass this state down to the Table component so that you can use a Array.filter when rendering your table.
function Page() {
const [activeTab, setActiveTab] = useState(DEFAULT_ACTIVE_TAB)
return (
<>
<Navbar activeTab={activeTab} setActiveTab={setActiveTab} />
<Table activeTab={activeTab} />
</>
)
}
Your Navbar component should have a onClick handler where it is calling the setActiveTab function when the active tab change.
Then in your Table component you should have something like this:
function Table({ activeTab }) {
return (
<table>
...
{listOfOptions
.filter(option => /* something depending on the activeTab */)
.map(option => <... />)
}
</table>
}

React - update state in the child component when parent changes

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>
);
}

How to link sort function from header icon to the column to be sorted in React

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;

Functions are not valid as a react child? - Need help to extract fetched data into table

Quick summary of my homework-project.
I have a REST API on my backend, which has a API method returning the Star Wars characters from an external API.
In my web Development, I am trying to fetch these data from my REST API, and I can see the data is actually shown in the browser developer tools, but I get another error.
"Warning: index.js:1437 Warning: Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it."
Can you please see what's going wrong in below .js file?
import React, {Component} from "react";
import {Table,} from 'react-bootstrap'
const urlCharacters = 'URL WORKS - BUT NOT SHOWING HERE.';
class Characters extends Component {
constructor(props) {
super(props);
this.state = {characters: []}
;
}
async componentDidMount() {
//async fetchCharactersFromBackend(){
const res = await fetch(urlCharacters);
const data = await res.json();
console.log(data);
const newData = data.map(e => JSON.parse(e.name));
this.setState([newData]);
}
render() {
if (!this.state.characters) {
return null
}
return (
<div>
<h2>Persons table</h2>
<Table striped bordered hover>
<thead>
<tr>
<th>Character Name</th>
</tr>
</thead>
<tbody>
{this.renderCharacters}
</tbody>
</Table>;
</div>
);
}
renderCharacters(characters) {
characters.forEach(e => {
return ("<tr><td>" + e.name + "</td></tr>")
});
}
}
/*
Persons = () => {
return (
<div>
<h2>Persons table</h2>
<Table striped bordered hover>
<thead>
<tr>
<th>Character Name</th>
</tr>
</thead>
<tbody>
{this.renderCharacters}
</tbody>
</Table>;
</div>
);
};
*/
export default Characters;
Do the following fixes and let me know if it works:
this.setState([newData]); --> this.setState({characters: newData})
{this.renderCharacters} --> {this.renderCharacters()}
update the renderCharacters to the following:
renderCharacters() {
return this.state.characters.map(e => {
return ("<tr><td>" + e.name + "</td></tr>")
});
}
}
{this.renderCharacters}
should be
{this.renderCharacters()}

Categories