I have multiple pods running on my Kubernetes cluster and I have a "core app" built with react from which I want to get CPU & Memory usage stats.
Right now I am testing using a very simple setup where I have a local node app using socket.io to stream the time (based on this tutorial)
However, with one component which looks like the following, I am able to get real time updates from the server.
import React, { useState, useEffect } from "react";
import socketIOClient from "socket.io-client";
import {StatsCPUWrapper} from './statsCPU.style'
const ENDPOINT = process.env.STATS_ENDPOINT || "http://127.0.0.1:4001";
function StatsCPUComp() {
const [cpustats, setCPUstats] = useState("");
useEffect(() => {
const socket = socketIOClient(ENDPOINT);
socket.on("FromAPI", data => {
setCPUstats(data);
});
// Clean up the effect
return () => socket.disconnect();
}, []);
return (
<StatsCPUWrapper>
<p>
It's <time dateTime={cpustats}>{cpustats}</time>
</p>
</StatsCPUWrapper>
);
}
export default StatsCPUComp;
What I am now trying to do is have 3 or more of those components (depends on the list I get from my backend) to "subscribe" to multiple servers at the same time.
Here's my "projects list" component which gets the stats from the initial state and renders all the details:
import React from 'react'
import {useSelector, useDispatch} from 'react-redux'
import {Link} from 'react-router-dom'
import PropTypes from 'prop-types'
import {create, remove} from '../../features/projects/projectSlice'
import {ProjectWrapper} from './project.style'
import StatsCPUComp from './stats/statsCPU'
export function ProjectComp() {
const dispatch = useDispatch()
const projects = useSelector((state) => state.projects)
const handleSubmit = (e) => {
e.preventDefault()
}
const handleAction = (e) => {
e.preventDefault()
}
return (
<ProjectWrapper>
<div className="projects">
<div className="row">
{projects.map((projects) => (
<div className="col-12">
<div class="card project-card">
<div className="card-body">
<div className="row">
<div className="col-4 project-text">
<h5 class="card-title">
{' '}
<Link to={`/projects/` + projects.id}>{projects.name}</Link>
</h5>
<p class="card-text">Owner: {projects.owner}</p>
<p class="card-text">{projects.email}</p>
</div>
<div className="col-4 projects-stats">
<StatsCPUComp />
</div>
<div className="col-4 projects-stats"></div>
<div className="col-4 projects-stats"></div>
</div>
</div>
</div>
<br></br>
</div>
))}
</div>
</div>
</ProjectWrapper>
)
}
Right now the "time" from the stats component is being added on my last project component (makes sense since I did not implement any approach yet to map that too).
Any ideas on how I can have a different stats component for each of my "projects" where each one connects to a provided endpoint ? (I can pass all of the endpoints as env variables)
Any help would be highly appreciated.
So here's the implementation I did to make it work. (Not sure if it's ideal so please feel free to make any suggestions)
I added "endpoint" to state.projects which holds the data I get from my backend.
Then in my "projects list" component mentioned shown in the question, I pass projects (from state.projects) as props
<StatsCPUComp props={projects}/>
I then destructure it and pass it to my useEffect() in the stats component as follows:
import React, {useState, useEffect} from 'react'
import socketIOClient from 'socket.io-client'
import {StatsCPUWrapper} from './statsCPU.style'
import {useSelector, useDispatch} from 'react-redux'
let ENDPOINTS = []
let PROJECTS = []
function StatsCPUComp(...props) {
const [cpustats, setCPUstats] = useState('')
let endpoints = {...props}
let endpoints_2 = {...endpoints[0]}
useEffect(() => {
let socketlist = []
console.log(endpoints[0].props.endpoint)
const socket = socketIOClient(endpoints[0].props.endpoint);
socket.on("FromAPI", data => {
setCPUstats(data);
});
return () => socket.disconnect();
}, [cpustats])
return (
<>
<StatsCPUWrapper>
<p>
It's <time dateTime={cpustats}>{cpustats}</time>
</p>
</StatsCPUWrapper>
</>
)
}
export default StatsCPUComp
It seems to be working fine, however please do provide any suggestions since I might not be following an optimal approach (Performance and scalability wise)
Related
I am on the way to learning Jest and React Testing Library.
When I run the test below, it is 'passed' but I see the console.error message.
[sectionIntro.spec.js]
import React from "react";
import { fireEvent, render, screen } from "#testing-library/react";
import SectionIntro from "../../components/home/SectionIntro.jsx";
import { CV_URL } from "../../lib/socials";
describe("SectionIntroComponent", () => {
it("Should open the CV link when CV button is clicked", () => {
render(<SectionIntro />);
const cv_link = screen.getByText(/Check My CV/i);
expect(cv_link.href).toBe(CV_URL);
});
});
// Also a mock file is used for an image file.
[SectionIntro.jsx]
import * as React from "react";
import Image from "next/image";
import profilePic from "../../public/me.png";
import styles from "../../styles/SectionIntro.module.css";
import { CV_URL } from "../../lib/socials";
const SectionIntro = () => {
return (
<>
<div>
<div>
<Image
className="rounded-full"
src={profilePic}
alt="The author of the website"
objectFit="cover"
sizes="30vw"
/>
</div>
</div>
<div>
<button
className={`${styles.btnCV}`}
>
<a
href={CV_URL}
aria-label="CV"
target="_blank"
rel="noopener noreferrer"
>
Check My CV
</a>
</button>
</div>
</>
);
};
export default SectionIntro;
[mocks/mockFile.js]
export default "";
My question is if the test result is passed why the console.error is shown?
Would it be okay to ignore console.error?
If it is not a good idea to hide the console, why is it so?
Thanks alot in advance!
I'm sorry in advance for my ignorance, But its been a while since I coded, and I need a little help with this basic thing, Im trying to reach name, and display it on screen when I click a button, but i just can't figure it out, can u help me and explain what you did so I can know for the next time? looked manuals and articles about how to do it but you guys are just simply better.
my code:
functional (pokeAPI.ts):
import React from "react";
import axios from 'axios'
export const getPokemon=()=>{
axios.get("https://pokeapi.co/api/v2/pokemon?limit=151").then((response)=>{
response.data.results.map((cont : string,index : number) => console.log(
cont
));
})
}
app.tsx:
import './App.css';
import React from 'react';
import {getPokemon} from './func/pokeAPI';
function App() {
return (
<div>
<button onClick={getPokemon}>Cli</button>
</div>
);
}
export default App;
Here you go! Since you're using typescript, I created a PokemonData interface as well.
import React from 'react'
import axios from 'axios'
import "./styles.css";
interface PokemonData {
name: string
url: string
}
export default function App() {
const [pokemons, setPokemons] = React.useState<null|PokemonData[]>(null)
const onClick = async () => {
axios.get("https://pokeapi.co/api/v2/pokemon?limit=151").then((response)=>{
console.log(response.data.results)
setPokemons(response.data.results)
})
}
return (
<div className="App">
<button onClick={onClick}>Get pokemon</button>
{pokemons?.map((pokemon) => (
<div key={pokemon.name}>
<p>{pokemon.name}</p>
<p>{pokemon.url}</p>
</div>
))}
</div>
);
}
I also wrote a Codesandbox here so you can check it out
Edit: Another version of the return statement if optional chaining isn't supported on your project's version
return (
<div className="App">
<button onClick={onClick}>Get pokemon</button>
{pokemons && pokemons.map((pokemon) => (
<div key={pokemon.name}>
<p>{pokemon.name}</p>
<p>{pokemon.url}</p>
</div>
))}
</div>
);
}
I dont know why when i want to render a component inside of a map function, basiclly i have a List component, and when i fetch data from an API with the email, etc.. from users i want that component to render that info. But i get the following error:
Unhandled Runtime Error
Error: Objects are not valid as a React child (found: object with keys {email, phone, nick}). If you meant to render a collection of children, use an array instead.
My List component looks like this:
import React from 'react'
export default function List(email, nick, phone) {
return (
<div align="center">
<hr />
<strong>Email: </strong>
<p>{email}</p>
<strong>Nick: </strong>
<p>{nick}</p>
<strong>Phone: </strong>
<p>{phone}</p>
</div>
)
}
And my List user page looks like this:
import React from 'react'
import Nav from '../../components/Nav/Nav'
import { useEffect, useState } from 'react';
import List from '../../components/User/List';
export default function index() {
const [users, setUsers] = useState([])
const fetchUsers = async () => {
const response = await fetch("http://localhost:3001/api/internal/users");
const data = await response.json();
console.log(data["data"])
setUsers(data["data"])
}
useEffect(() => {
fetchUsers()
}, [])
return (
<div>
<Nav />
{users.map(user => (
<List
email={user.attributes.email}
phone={user.attributes.phone}
nick={user.attributes.nick}
/>
))}
</div>
)
}
UPDATE 21 ABR
For some reason when i do this :
export default function List(email, phone, nick) {
return (
<div align="center">
<hr />
<strong>Email: </strong>
<p>{email.email}</p>
<strong>Nick: </strong>
<p>{email.phone}</p>
<strong>Phone: </strong>
<p>{email.nick}</p>
</div>
)
}
It works... Someone knows what it can be?
You are passing the props in a wrong way. Either use it as a single object in props or have all the props it inside {} using destructuring method.
export default function List({email, phone, nick}) {}
OR
export default function List(props) {
return (
<div align="center">
<hr />
<strong>Email: </strong>
<p>{props.email}</p>
<strong>Nick: </strong>
<p>{props.phone}</p>
<strong>Phone: </strong>
<p>{props.nick}</p>
</div>
)
}
Below is my code, the React app is connected to Node js and the data of comment are saved when submit but it's not working for StarRating component.
The comment data is saved in the db table but not the rating
Please pass setRating in starComponent as props
Like below:
<StarRating rating={rating1} updateRating={(e) => setRating1(e)}
onChange={e => setRating1(e.target.value)}></StarRating>
Now you will get updateRating as props in starComponent. So update rating form star component like below:
import React, { useState} from "react";
import { FaStar } from 'react-icons/fa';
const StarRating = ({rating, updateRating}) =>{ // Here you will get setRating state(State of app component) in props
// const [rating, setRating] = useState(null); // not needed this state here. Update rating value in state which comes form props
const [hover, setHover] = useState(null);
return <div>
<p>Rate your experience from 0 to 5 stars</p>
{[... Array(5)].map((star, i)=>{
const ratingValue= i + 1;
return (
<label>
<input
type="radio"
name="rating"
value={rating}
onClick={() =>updateRating(ratingValue)} /> // Update `updateRating` state which comes from app component.
<FaStar
className="star"
color={ratingValue <= (hover || rating) ? "#11C4B0" : "#D3D3D3"}
size={40}
onMouseEnter={() =>setHover(ratingValue)}
onMouseLeave={() =>setHover(null)}
/>
</label>
);
})}
</div>
}
export default StarRating;
You will get updated state in rating1 in app component if any changes occurred from starComponent.
I think the Problem is that you are accessing the rating state in App component but the real state with the value is the rating state of StarRating component. Also, you have passed the props onChange and value to StarRating component but The Props concept is different than the HTML Attributes concept so you definitely need to look into that. Anyway, the possible Solution can be...
import * as React from 'react';
import './App.css';
import StarRating from './StarRating';
import StarRating2 from './StarRating2';
import StarRating3 from './StarRating3';
import { TextArea } from 'semantic-ui-react';
import AlertDialogSlide from './confirmation';
import Dialog from '#mui/material/Dialog';
import DialogActions from '#mui/material/DialogActions';
import DialogContent from '#mui/material/DialogContent';
import DialogContentText from '#mui/material/DialogContentText';
import Slide from '#mui/material/Slide';
import Button from '#mui/material/Button';
import { useState } from "react";
const Transition = React.forwardRef(function Transition(props, ref) {
return <Slide direction="up" ref={ref} {...props} />;
});
function App() {
const [open, setOpen] = React.useState(false);
const [comment, setComment] = useState("");
const [rating1, setRating1] = useState("");
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
const onSubmitForm = async e => {
e.preventDefault();
try {
const body = { rating1, comment };
const response = await fetch("http://localhost:5000/feedback", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body)
});
window.location = "/";
} catch (err) {
console.error(err.message);
}
};
return (
<form onSubmit={onSubmitForm} >
<div className="App">
<img src='solavievelogo.png'></img>
<hr/>
<h2>Leave a feedback!</h2>
<StarRating setRating={setRating1} />
<hr2/>
<StarRating2></StarRating2>
<hr2/>
<StarRating3></StarRating3>
<hr2/>
<p>Please leave a comment about your experience below:</p>
<TextArea placeholder=' Type your comment here...'
value={comment}
onChange={e => setComment(e.target.value)}
></TextArea>
<br/>
<button class="Button" type='submit' variant="outlined" onClick={handleClickOpen}><span class="Button-inner">SEND FEEDBACK</span> </button>
<Dialog
open={open}
TransitionComponent={Transition}
keepMounted
onClose={handleClose}
aria-describedby="alert-dialog-slide-description"
>
<DialogContent>
<img src='confirm.png'></img>
<DialogContentText id="alert-dialog-slide-description">
<p>Thank you for your message!</p>
<p> We will be in contact soon..</p>
</DialogContentText>
</DialogContent>
<DialogActions >
<button class="Button" type='submit' onClick={handleClose} ><span class="Button-inner">Close</span> </button>
</DialogActions>
</Dialog>
</div>
</form>
);
}
export default App;
StarRating Component
import React, { useState} from "react";
import { FaStar } from 'react-icons/fa';
const StarRating = ({setRating}) =>{
const [hover, setHover] = useState(null);
return <div>
<p>Rate your experience from 0 to 5 stars</p>
{[... Array(5)].map((star, i)=>{
const ratingValue= i + 1;
return (
<label>
<input
type="radio"
name="rating"
value={ratingValue}
onClick={() =>setRating(ratingValue)} />
<FaStar
className="star"
color={ratingValue <= (hover || rating) ? "#11C4B0" : "#D3D3D3"}
size={40}
onMouseEnter={() =>setHover(ratingValue)}
onMouseLeave={() =>setHover(null)}
/>
</label>
);
})}
</div>
}
export default StarRating;
So I have a component(Posts) in which a loop through all the posts from the Database. In the component, I 'embedded' another component(PostItem) so I dont have to create a different component for viewing individual entries. Now inside the PostItem component I have another component with the name of 'PostGallery'.
This is the current code that I have in PostItem.js file:
import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import PostGallery from '../posts/PostGallery';
const PostItem = ({
post: { _id, text, name, files, avatar, user, likes, comments, date },
}) => (
<article className={_id}>
<Link to={`/posts/${_id}`}>
<div id="carouselExampleIndicators" className="carousel slide" data-ride="carousel">
<div className="carousel-inner">
{files.slice(0, 5).map((file, index) => (
<PostGallery key={index} post={file} />
))}
</div>
</div>
</Link>
</article>
);
PostItem.propTypes = {
post: PropTypes.object.isRequired,
};
export default connect(
null, null
)(PostItem);
When posting an entry the user can post URL from images separated by comma which is working just fine. The problem comes when displaying it in the front-end.
This is what I have in my PostGallery component:
import React from 'react';
import PropTypes from 'prop-types';
const PostGallery = ({
post: { files }
}) => {
return (
<div className="">
{post.files.length > 0 ? (
post.files.map(file => (
<img key={file} src={file} alt="" />
))) : (
<p>No images found</p>
)
}
</div>
);
};
PostGallery.propTypes = {
post: PropTypes.object.isRequired,
};
export default PostGallery;
I believe this should be easy but somehow its just now working and the console it's not trowing me any errors related to it. So if you guys can help...
Thanks!