How to pass props through router - reactjs

I have a list of task, on my dashboard, I want as soon as clicked on a task to get his details.
At the moment I can only retrieve the id of this task, but can't get other information.
When I console.log listTask on my parent component, I get an Array objects that contains all the task:
[ 0: Object { state: "Started", date: "2019-02-11T19:57:26.176Z", _id: "5c6074afd2f8eb5019fd5f54", … }
1: Object { state: "Started", date: "2019-02-11T19:57:26.176Z", _id:"5c60798bacf119510c19f5b1", … }
2: Object { state: "Started", date: "2019-02-11T19:57:26.176Z", _id: "5c607be3c11c2b529615ac26", … }
3: Object { state: "Started", _id: "5c607cfec27d6254c6fe0ca1", projectName: "I LOVE THIS GAME", … } ]
parentComponent.js
async componentDidMount() {
this._isMounted = true
let response = await fetch(`http://localhost:5001/userproject/${taskId}`)
let data = await response.json()
console.log(data) // will show me the array
const projects = data.map(pro => {
return {
id: pro._id,
state: pro.state,
projectName: pro.projectName,
consultants: pro.consultants,
ScrumMasterUsername: pro.scrumMaster.username,
ScrumMasterId: pro.scrumMaster._id,
}
})
return this.setState({
projects: { projects },
})
}
render() {
const { projects } = this.state
const list = (projects && projects.projects) || []
if (!projects) {
return null
}
return (
<div className="dashboard container">
<div className="row">
<div className="col s16 m7 darken-2">
<ProjectList
name={this.state.projects.projectName}
projects={list}
/>
</div>
<div className="col s12 m3 offset-m1">
<Notifications />
</div>
</div>
</div>
)
}
My problem: When tried to get the task details, it retrieves the id of this task and not the other detail:
import React from 'react'
const ProjectDetails = props => {
const id = props.match.params.id
console.log(props) // i can only get the id
return (
<div className="container section project-details">
<div className="card z-depth-0">
<div className="card-content">
<span className="card-title">Project Title - {id} </span>
</div>
</div>
</div>
)
}
export default ProjectDetails

This is how you should do it.
class App extends React.Component {
render() {
return (
<Router>
<div>
<Switch>
<Route path="/" component={Home} />
**<Route path="/about" render={(props) => <About {...props} isAuth={"Authenticated"} />} />**
<Route path="/topics" component={Topics} />
</Switch>
</div>
</Router>
);
}
}
The second route marked between stars, is the one where I am rendering the component and passing a prop isAuth={"Authenticated"}
The above prop will be available inside About component as
const About = (props) => {
return (
<div>
<h1>About={props.isAuth}</h1>
</div>
);
}
Hope this helps you!!

Related

Remove item from array react js

I'm having trouble with removing an object from an array. I have tried many options and nothing works.
Please help me solve this problem.
Thanks for your help
My array:
export const SavedList = [
{
word: 'hello',
translate: 'привет',
note: 'say say'
}
]
Remove callback (deletePost)
class Menu extends Component {
state = {
word: '',
translate: '',
note: '',
data: SavedList,
}
deletePost =(id)=>{
this.setState({
data : this.state.data.filter((el)=> el.id !== id)
})
}
render() {
return (
<div className="content">
<Routes>
<Route path="/" element={<Layout />}>
<Route path="saved" element={<Saved data={this.state.data} del={this.deletePost}/>}/>
</Route>
</Routes>
<div>
</div>
</div>
);
}
}
export default Menu;
here button delete with onClick
class Saved extends Component {
render() {
const sl = this.props.data.map((sl, id) => {
return (
<div className="saved-card" key={id}>
<div className="content">
<p>{id}</p>
<p>{sl.word}</p>
<p>{sl.translate}</p>
<p>{sl.note}</p>
</div>
<div className="btn-block">
<button
onClick={() => this.props.del(id)}
type="button"
className="delete-btn"
>
delete
</button>
</div>
</div>
);
});
return (
<div>
<h2>Saved list</h2>
<div className="saved-inner">
<div className="saved-list">{sl}</div>
</div>
</div>
);
}
}
export default Saved;
This issue is because el.id doesn't have any value in delete Post Function. That function also needs to have an ID variable, I have named it as i_di.
class App extends Component {
state = {
word: '',
translate: '',
note: '',
data: SavedList,
}
deletePost =(id)=>{
this.setState({
data : this.state.data.filter((el, i_di)=> i_di !== id )
})
}
render() {
return (
<div className="content">
{<Saved data={this.state.data} del={this.deletePost}/>}
</div>
);
}
}
export default App;

How can I insert row div for every 3rd entry in props?

I've got a set of dashboards that display in bootstrap cards on a front page and I would like to wrap them in a div with the class row for every 3rd entry. I was thinking about marking my dashboard component with the DB id from props and use a modulus function, but that will cause problems if an ID is deleted
Dashboard component:
export type DashboardProps = {
id: number
title: string
description: string
}
const Dashboard: React.FC<{ dashboard: DashboardProps }> = ({ dashboard }) => {
return (
<>
<div className="col-sm-12 col-lg-4">
<div className="card bg-light h-100">
<div className="card-header">
{dashboard.title}
</div>
<div className="card-body d-flex flex-column">
<p className="card-text">
{dashboard.description}
</p>
<a className="btn btn-info text-center mt-auto"
onClick={() =>
Router.push("/dashboard/[id]", `/dashboard/${dashboard.id}`)
}
>Go to dashboard</a>
</div>
</div>
</div>
</>
)
}
export default Dashboard
Index page:
type Props = {
dashboards: DashboardProps[]
}
export const getServerSideProps: GetServerSideProps = async () => {
const dashboards = await prisma.dashboard.findMany({
orderBy: { id: "asc", },
})
return {
props: JSON.parse(JSON.stringify({ dashboards })),
}
}
const Index: React.FC<Props> = (props) => {
const { data: session, status } = useSession()
if (status === "loading") {
return (
<Spinner />
)
}
if (!session) {
return (
<Layout>
<AccessDenied />
</Layout>
)
}
return (
<Layout>
<h1>Dashboards</h1>
{props.dashboards.map((dashboard) => (
<Dashboard dashboard={dashboard} />
))}
</Layout>
)
}
export default Index
I could also potentially wrap them in a single div with class row, but I would need to enforce a top/bottom margin so the cards don't stack right on top of each other. Any tips to get me rolling on this would be greatly appreciated!
.map provides index, you this to find every 3rd element.
//...
{
props.dashboards.map((dashboard, index) =>
(index + 1) % 3 === 0 ? (
<div>
<Dashboard key={dashboard.id} dashboard={dashboard} />
</div>
) : (
<Dashboard key={dashboard.id} dashboard={dashboard} />
)
)
}

Some Problem in Params, Not getting the Emp id ? possible solution

Error Are : Uncaught TypeError: Cannot read properties of undefined (reading 'params'),
The above error occurred in the component:
I might think i problem is in the passing props
App component
class App extends Component {
render() {
return (
<BrowserRouter>
<Routes>
<Route exact path="/" element={<Navigate to={"/card"} />}></Route>
<Route path="/card" element={<Card />} />
<Route path="/edit/:id" element={<EditEmployee />} />
</Routes>
</BrowserRouter>
);
}
}
card Component
const emp1 = {
empId: 100,
empName: "Jack",
age: 30,
salary: 50000,
image: image1,
achievements:
"Has got 3 bravo awards and 1 MVP award. Has worked on cutting edge technologies as well",
};
const emp2 = {
empId: 101,
empName: "Jane",
age: 24,
salary: 40000,
image: image2,
achievements: "No major achievements so far",
};
export default class Card extends React.Component {
empArr = [emp1, emp2];
render() {
return (
<div>
<h3 className="text-center text-primary">Employee Details</h3>
<div className="row">
{this.empArr.map((emp) => (
<Employee key={emp.empId} emp={emp} />
))}
</div>
</div>
);
}
}
Employee Component
class Employee extends React.Component {
state = {
achievements: null,
edit: null,
};
handleEdit = () => {
this.setState(() => ({ edit: true }));
};
handleView = () => {
this.setState(() => ({ achievements: this.props.emp.achievements }));
};
render() {
const { emp } = this.props;
if (this.state.edit) {
return <Navigate to={"/edit/" + emp.empId} push></Navigate>;
}
return (
<div className="card" style={{ width: 200 }}>
<img
className="card-img-top"
src={emp.image}
height="200"
alt="Card cap"
/>
<div className="card-body">
<h5 className="card-title text-center">{emp.empName}</h5>
<p className="card-text">
<span>Id: {emp.empId}</span>
<br />
<span>Age: {emp.age}</span>{" "}
{emp.age < 25 && <span className="text-info"> - Fresher</span>}
<br />
<span>Salary: {emp.salary}</span>
<br />
</p>
<p>
<i>{this.state.achievements}</i>
</p>
<button
type="button"
className="btn btn-primary"
onClick={this.handleEdit}
>
Edit
</button>
<button className="btn btn-success" onClick={this.handleView}>
View
</button>
</div>
</div>
);
}
}
EditEmployee Component
class EditEmployee extends React.Component {
render() {
return <h3>The selected ID is {this.props.match.params.id}</h3>;
}
}
export default EditEmployee;
Result should be like this
WHAT I WANT
but i am getting this error on click edit
enter image description here
WHAT I AM GETTING /ERROR IMAGE
You want to get the params via the useParams() hook:
const EditEmployee = () => {
const id = useParams()
return (
<h3>The selected ID is {id}</h3>;
)
}
export default EditEmployee;
Sorry that's a functional component. You can translate it to a class...
Did you just console.log(this.props) in your EditEmployee to see what you're getting?
You may need to wrap your class component in a function component as illustrated here.
Documentation here

How can I get a component to update on path change while using the same route and component?

When the user is viewing another users profile, and attempt to click their own profile link in the navbar, the component never updates. In both the Redux and React dev tools, it shows that the state has been updated correctly but the component doesnt seem to notice and update.
class App extends Component {
render() {
return (
<Router>
<div>
<Navbar />
<NavbarFix />
<Switch>
<Route exact path="/" component={Posts} />
<Route exact path="/submit" component={AuthRoute(Submit)} />
<Route exact path="/signup" component={AlreadyAuth(SignUp)} />
<Route exact path="/login" component={AlreadyAuth(LogIn)} />
<Route exact path="/user/:id" component={AuthRoute(User)} />
<Route exact path="/item/:id" component={AuthRoute(Item)} />
<Route exact path="/admin" component={AdminAuth(Admin)} />
<Route exact path="/banned" component={Banned} />
<Route component={NoMatch} />
</Switch>
</div>
</Router>
);
}
}
.
class User extends Component {
constructor(props) {
super(props);
this.state = {
posts: [],
user: [],
comments: []
};
}
componentDidMount() {
this.loadUser();
}
loadUser = () => {
API.findUserById(this.props.match.params.id)
.then(res => {
console.log(res.data);
this.setState({
user: res.data.user,
posts: res.data.user.Posts,
comments: res.data.comments
});
console.log(this.state)
})
.catch(err => console.log(err));
this.setState(this.state)
}
handleCommentDelete = id => {
API.deleteComment(id)
.then(res => this.loadUser())
.catch(err => console.log(err));
}
handlePostDelete = id => {
API.deletePost(id)
.then(res => this.loadUser())
.catch(err => console.log(err));
}
render() {
return (
<div>
<div className="container-fluid">
<div className="row">
<div className="col-4 user-data-container">
<div className="row">
<div className="col-12 text-center">
<h2>{this.state.user.username}'s Posts</h2>
</div>
</div>
<hr className="pb-4" />
<div className="row">
<div className="col-12">
{this.state.posts.length > 0 ?
this.state.posts.map(post => (
<PostContainer handledelete={this.handlePostDelete} post={{ ...post, User: this.state.user }} key={post.id} check={this.props.id} />
))
:
<h1>No Posts To Show!</h1>
}
</div>
</div>
</div>
<div className="col-4 user-data-container">
<div className="row">
<div className="col-12 text-center">
<h2>{this.state.user.username}'s Comments</h2>
</div>
</div>
<hr className="pb-4" />
<div className="row">
<div className="col-12">
{this.state.comments.length > 0 ?
this.state.comments.map(comments => (
<CommentContainer verified={this.state.user.verified} handledelete={this.handleCommentDelete} check={this.props.id} comment={comments} className="hover-effect single-comment" key={comments.id}/>
))
:
<h1>No Comments To Show!</h1>
}
</div>
</div>
</div>
<div className="col-4 user-data-container">
<div className="row">
<div className="col-12 text-center">
<h2>{this.state.user.username}'s Information</h2>
</div>
</div>
<hr className="pb-4" />
<div className="row">
<div className="col-12">
<UserContainer user={this.state.user}/>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
}
const mapStateToProps = state => {
return {
username: state.auth.username,
id: state.auth.id,
email: state.auth.email,
profileImg: state.auth.profileImg,
verified: state.auth.verified
};
};
export default withRouter(connect(mapStateToProps)(User));
I believe this has to do with the same route and component being used, so the change isn't actually noticed. Is there any way to fix this? To my understanding, the component should be updating on state change.
If the link is directing to the same route with just a different param, it will not remount/componentDidMount will not be called again.
So, you could use the componentWillReceiveProps(newProps) lifecycle function and look for newProps.match.params.id and compare it with this.props.match.id and if different call loadUser again.
**You would also need to change your loadUser function to accept an id param
(Param validation not included)
componentDidMount() {
this.loadUser(this.props.match.id);
}
loadUser = (id) => {
API.findUserById(id)..
…
}
componentWillReceiveProps(newProps) {
if(newProps.match.params.id !== this.props.match.id) {
this.loadUser(newProps.match.params.id)
}
}

React - Link redirect to item but not refresh the component

When I click StageItem 'Details', URL is being changed but Stage component not refresh the data. I was trying to use Switch and withRouter, without success, because I completely don't know how to use these in my case. Thanks in advance for answers. My code:
App
class App extends Component {
render() {
return (
<BrowserRouter>
<div className="App">
<Header/>
<div className="container">
<div className="row">
<Route path='/' component={InputContainer}/>
<Route path='/' component={() => <StagesList todos={this.props.todos}/>} />
<Route path='/:stage_id' render={(props) => <Stage {...props} todos={this.props.todos}/>} />
</div>
</div>
</div>
</BrowserRouter>
);
}
}
const mapStateToProps = state => {
return {
todos: state.todos
};
};
const mapDispatchToProps = {};
export const AppContainer = connect(mapStateToProps, mapDispatchToProps)(App);
StageItem
export const StageItem = ({ todo, id, handleRemoveTodo}) => {
return(
<li className="stage_item" key={id}><span onClick={() => handleRemoveTodo(id)}><FontAwesomeIcon className="fa" icon="trash" /> {todo.tour} - {todo.duration.hours}:{String(todo.duration.minutes).padStart(2,"0")}:{String(todo.duration.seconds).padStart(2,"0")} - {todo.distance} km - {todo.avgSpeed} km/h - {todo.maxSpeed} km/h</span><Link to={'/'+todo.id}>Details</Link></li>
)
};
StagesList
export class StagesListC extends React.Component{
render(){
return(
<div className="col-12 col-sm-12 col-md-12 col-lg-12">
<SectionTitle title="Stage list"/>
{this.props.todos.length>0 ?
<ul className="todo-list">
{this.props.todos.map((todo) => <StageItem
key={todo.id}
id = {todo.id}
todo={todo}
handleRemoveTodo = {this.props.handleRemoveTodo}
/>)}
</ul>
:
<div>You have no stages</div>}
</div>
)
}
}
const mapStateToProps = dispatch => bindActionCreators({
handleRemoveTodo
}, dispatch)
const mapDispatchToProps = { handleRemoveTodo };
export const StagesList = connect(mapStateToProps, mapDispatchToProps)(StagesListC);
Full project: Link
You need to add exact to your / routes if you don't want to see the InputContainer and StagesList while you are at :stage_id route.
<div className="row">
<Route path='/' exact component={InputContainer}/>
<Route path='/' exact component={() => <StagesList todos={this.props.todos}/>} />
<Route path='/:stage_id' render={(props) => <Stage {...props} todos={this.props.todos}/>} />
</div>
There is also a bug in your Stage component.
todo.id is an integer, but stage_id is a string. To fix it you can convert both to string, e.g.
let stage = todos.filter((todo) => String(todo.id) === String(id));
UPD:
If you want to see StageList and Stage components at all time, you need to fix the Stage component. The Stage component updates its state.stage value only once, when componentDidMount is fired. All subsequent updates do not update stage.stage value. You can implement componentWillReceiveProps lifecycle method and update stage.stage value whenever props are changed. Alternatively, you don't use the state.stage and compute stage value on the fly in render method, like so:
class Stage extends React.Component {
getStage = () => {
const todos = this.props.todos || [];
let id = this.props.match.params.stage_id;
return todos.filter((todo) => String(todo.id) === String(id))[0];
}
render() {
const stage = this.getStage();
if (!stage) return null;
return <div className="col-12 col-sm-12 col-md-12 col-lg-12">
<SectionTitle title="Single stage"/>
<div>Id: {stage.id}</div>
<div>Distance: {stage.distance}</div>
<div>Duration: {stage.duration.hours}:{String(stage.duration.minutes).padStart(2, "0")}:{String(stage.duration.seconds).padStart(2, "0")}</div>
<div>Average speed: {stage.avgSpeed} km/h</div>
<div>Max speed: {stage.maxSpeed} km/h</div>
</div>
}
}

Resources