React app: Images from firebase are not shown on a page - javascript

I want to display images on a page that come from firebase. I can successfully get urls from the storage but when I use them in image as a src, they (images) are not shown.
As you can see from the image above, image tags are blank. I don't know what is the problem. Could suggest me a way out. My code is the following:
import React, { Component } from 'react';
import { firebase } from '../../../firebase';
import AdminLayout from '../../../Hoc/AdminLayout';
import { firebasePhotos } from '../../../firebase';
import { firebaseLooper, reverseArray } from '../../ui/misc';
import { css } from 'react-emotion';
import { BarLoader } from 'react-spinners';
import { confirmAlert } from 'react-confirm-alert';
import 'react-confirm-alert/src/react-confirm-alert.css';
class Adminphoto extends Component {
state = {
isLoading: true,
photos: [],
marginTop: '40px',
successForm: ''
};
componentDidMount() {
firebasePhotos.once('value').then(snapshot => {
const photos = firebaseLooper(snapshot);
this.setState({
isLoading: false,
marginTop: '0px',
photos: reverseArray(photos)
});
});
}
getURL = filename => {
//console.log(filename);
firebase
.storage()
.ref('photos')
.child(filename)
.getDownloadURL()
.then(url => {
console.log('url =>' + url);
return url;
});
};
successForm(message) {
this.setState({
successForm: message
});
setTimeout(() => {
this.setState({
formSuccess: ''
});
}, 2000);
}
deleteItem(event, photo) {
event.preventDefault();
confirmAlert({
title: 'Confirm to submit',
message: 'Are you sure to do this.',
buttons: [
{
label: 'Yes',
onClick: () => {
firebasePhotos
.child(photo.id)
.remove()
.then(() => {
this.successForm('Removed successfully');
this.props.history.push('/admin_photo');
});
}
},
{
label: 'No',
onClick: () => {
return false;
}
}
]
});
}
render() {
console.log(this.state.photos);
const override = css`
display: block;
margin: 0 auto;
border-color: red;
`;
return (
<AdminLayout>
<React.Fragment>
<div
className="has-text-centered"
style={{ marginTop: this.state.marginTop }}
>
{this.state.isLoading ? (
<BarLoader
className={override}
sizeUnit={'px'}
size={50}
width={100}
height={4}
color={'#2D7969'}
loading={this.state.loading}
/>
) : (
''
)}
</div>
<div className="columns">
{this.state.photos
? this.state.photos.map((photo, i) => (
<div key={i} className="column">
<img src={this.getURL(photo.image)} />
</div>
))
: null}
</div>
</React.Fragment>
</AdminLayout>
);
}
}
export default Adminphoto;

changes i made:
1. this.state.photos to be the img src
2. this.getURL() is called in componentDidMount()
3. <img> gets src directly from state
componentDidMount() {
firebasePhotos.once('value').then(snapshot => {
const photos = firebaseLooper(snapshot);
reverseArray(photos).map(photo => {
this.getURL(photo.image)
})
this.setState({
isLoading: false,
marginTop: '0px',
})
});
}
getURL = filename => {
//console.log(filename);
firebase
.storage()
.ref('photos')
.child(filename)
.getDownloadURL()
.then(url => {
this.setState({ photos: [...this.state.photos, url] })
});
};
render() {
...
this.state.photos.map((photo, i) => (
<div key={i} className="column">
<img src={photo} />
</div>
))
...
hope it works, lemme know if im not really clear at explaining

One way to do this is to save url of image in the firebase realtime database and then get them from database and save them in the state.
You can manually save the urls in the database while uploading them or write cloud function triggers which will save urls in the database, every time image is uploaded in the firebase storage.

Related

react setState() from external?

New to Reactjs, I have followed a tut or 2 to build a relatively simple app, that sends queries to Mongodb and renders the results. Although I am yet to render them. I can pass the find() through and get back results that I like, and log them to the console, but for the life of me I cannot figure out how to get the results into "state", or anywhere else in the app. It's likely a very simple mistake somewhere. But I don't have enough knowledge of react to figure it out.
Here is the (small) App.js file in it's entirety, I thought it easier than trying to pick through it and make a valid sample.
// /client/App.js
import React, { Component } from 'react';
import axios from 'axios';
import * as PropTypes from "prop-types";
import {useEffect, useState} from "react";
function View(props) {
return null;
}
View.propTypes = {children: PropTypes.node};
function Text(props) {
return null;
}
function MyForm() {
const [user_search, setName] = useState("");
const handleSubmit = async (event) => {
event.preventDefault();
console.log(`The Search you entered was: ${user_search}`);
let felcher = user_search.split(/[ ,]+/);
let search_obj = {}
for (let i in felcher) {
search_obj[i] = felcher[i]
}
axios.post('http://localhost:3001/api/searchData', {
search_obj
}
).then(resp => {
console.log("RESPONSE FROM POST", resp['data'])
});
}
return (
<form onSubmit={handleSubmit}>
<label>Enter Search Terms:
<input
type="text"
value={user_search}
onChange={(e) => setName(e.target.value)}
/>
</label>
<input type="submit" />
</form>
)
}
let formatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',})
Text.propTypes = {children: PropTypes.node};
class App extends Component {
// initialize our state
state = {
data: [],
_id: 0,
ticker: '',
primary_share: [],
title: null,
document_date: null,
release_date: null,
search_text: null,
url: null,
result_state: null,
};
componentDidMount() {
this.getDataFromDb();
if (!this.state.intervalIsSet) {
let interval = setInterval(this.getDataFromDb, 1000);
this.setState({ intervalIsSet: interval });
}
}
componentWillUnmount() {
if (this.state.intervalIsSet) {
clearInterval(this.state.intervalIsSet);
this.setState({ intervalIsSet: null });
}
}
getDataFromDb = () => {
fetch('http://localhost:3001/api/getData')
.then((data) => data.json())
.then((res) => this.setState({ data: res.data }));
};
render() {
const { data } = this.state;
return (
<div>
<MyForm />
<div class={"row"}>
<div class={"col-4"}>
{/*<ul>*/}
{/* {data.length <= 0*/}
{/* ? 'Getting Results......'*/}
{/* : data.map((dat) => (*/}
{/* <li class="border" style={{ padding: '10px' }} key={dat._id}>*/}
{/* <span style={{ color: 'gray' }}> Ticker: </span> {dat.ticker} <br />*/}
{/* <span style={{ color: 'gray' }}> Release Date: </span> {dat.release_date} <br />*/}
{/* <span style={{ color: 'gray' }}> Document Title: </span>{dat.title} <br />*/}
{/* <span style={{ color: 'gray' }}> Document URL: </span>{dat.url} <br />*/}
{/* </li>*/}
{/* ))}*/}
{/*</ul>*/}
</div>
</div>
</div>
);
}
}
export default App;
The area I am struggling with is where print the results to the console here ...
console.log("RESPONSE FROM POST", resp['data'])
In the "MyForm()" function. I feel if I could setState() there, but it appears to not work.
But I can't do anything else that gets them over to render. HELP!!!!
SetState is a hook that returns two items: the state and setter (or the function to set the state). In your case you will have a setState at the top of your function:
const [data, setData] = useState([]) // what ever you put as an argument will be the default data until it is set
const [err, setErr] = useState(null) // this will be our error state
In your axios request you will use:
axios
.post('http://localhost:3001/api/searchData', { search_obj })
.then(resp => {
setData(resp['data']) // see here we call the state function
})
.catch(err => {
setErr(err) // and here for our error
})
Then in our return we can use the data any way we like:
return (
<>
<div>{data}</data>
<div>{err ? err : 'no errors'}</div>
</>
)
Does that make sense? (Code not tested)

How to do mapping in reactjs if data is fetch in other file?

I have created a fileone.js file and I am trying to fetch the API data in this file as given below
import React, { Component, Suspense, lazy } from "react";
import axios from "axios";
import { ApiUrlConstant } from '../../../../utils/ApiUrlConstant';
import { getTokenu, isLoginu } from "../../../../utils/Token";
const Course = lazy(()=> import('../../../Course'));
const CourseDetails = lazy(()=> import('../../../CourseDetails'));
import Switch from "react-bootstrap/esm/Switch";
import PublicRoute from "../../../Route/Publicroute";
import FallBack from "../../FallBack/FallBack";
import "./Courses.scss";
class fileone extends Component {
constructor(props) {
super(props);
this.state = {
load: false,
checkPaid: true,
page: 0,
};
this.token = getTokenu();
}
setLoader = () => {
this.setState({ load: !this.state.load });
};
componentDidMount() {
axios.get(ApiUrlConstant.getApiFullUrl("course.feed"),
{
headers: {
Authorization: "Token " + this.token
}
})
.then((res) => {
this.setState({ User: res.data.results[0].user_detail.username, load: false });
this.setState({ Paid: res.data.results[0].user_paid, load: false });
this.setState({ CoursList: res.data.results[0].courses_list, load: false });
})
.catch((error) => {
this.setState({ error: error, load: false });
});
};
render() {
return (
<div className="course-main-feed">
<Suspense fallback={<FallBack />}>
<Switch style={{ paddingLeft: "0px" }}>
{ this.state.User && (this.state.Paid == true) ? <>
<PublicRoute restricted={false} component={Course} path="/paidcourses" handleToast={this.props.handleToast} exact/>
<PublicRoute restricted={false} component={CourseDetails} path="/paidcourses/:paidcourse_id" handleToast={this.props.handleToast} />
</>: null }
</Switch>
</Suspense>
</div>
);
}
}
export default fileone;
In the given code as we can see there is a line inside componentdidmount this.setState({ CoursList: res.data.results[0].courses_list, load: false }); in this line there are n number of courses object list. I am trying to map this courses_list in another file. I tried with the below code but I seems like the data is not getting in this
import React, { Component } from 'react'
import "./Course.scss";
export class Course extends Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
this.state = {
data: [],
load: false,
error: null,
next: "",
prev: "",
menu: false,
};
}
setLoader = () => {
this.setState({ load: !this.state.load });
};
renderArticlesFeed = () => {
if (this.state.CoursList === 0)
return (
<div style={{ width: "100%", display: "flex", justifyContent: "center", color: "#f3990f" }}>
<span>No Courses to display!</span>
</div>)
return this.state.CoursList && this.state.CoursList.map((item, i) => {
return (
<div className="" style={{width: "100%", margin: "none"}} key={item.id}>
<div className="course-feed-column">
<div className="">
{item.title}
</div>
</div>
</div>
);
}
)
};
render() {
const { data, error, load } = this.state;
return (
<div>
{this.renderArticlesFeed()}
</div>
)
}
}
export default Course
I am not able to get the value of {item.id} and {item.title}. Any help from anyone will be appreciated.
I can't see from where you get the courseList in state. If you want to pass courseList from one component to another you could pass it as a prop

Antd Design EditableRow not changing buttons from "edit" to "save" and "cancel"

Im using Antd library and i can't seem to find where i have the bug.
This is my EditableTableCell component
import React, {Component} from 'react';
import { Form } from '#ant-design/compatible';
import '#ant-design/compatible/assets/index.css';
import { Input, InputNumber, Select, DatePicker } from "antd";
import moment from "moment";
import {EditableContext} from "./EditableTableRow";
const FormItem = Form.Item;
const Option = Select.Option;
class EditableTableCell extends Component {
getInput = (record, dataIndex, title, getFieldDecorator) => {
switch (this.props.inputType) {
case "number":
return (
<FormItem style={{ margin: 0 }}>
{getFieldDecorator(dataIndex, {
rules: [
{
required: true,
message: `Please Input ${title}!`
}
],
initialValue: record[dataIndex]
})(
<InputNumber formatter={value => value} parser={value => value} />
)}
</FormItem>
);
case "date":
return (
<FormItem style={{ margin: 0 }}>
{getFieldDecorator(dataIndex, {
initialValue: moment(record[dataIndex], this.dateFormat)
})(<DatePicker format={this.dateFormat} />)}
</FormItem>
);
case "select":
return (
<FormItem style={{ margin: 0 }}>
{getFieldDecorator(dataIndex, {
initialValue: record[dataIndex]
})(
<Select style={{ width: 150 }}>
{[...Array(11).keys()]
.filter(x => x > 0)
.map(c => `Product ${c}`)
.map((p, index) => (
<Option value={p} key={index}>
{p}
</Option>
))}
</Select>
)}
</FormItem>
);
default:
return (
<FormItem style={{ margin: 0 }}>
{getFieldDecorator(dataIndex, {
rules: [
{
required: true,
message: `Please Input ${title}!`
}
],
initialValue: record[dataIndex]
})(<Input />)}
</FormItem>
);
}
}
render() {
const { editing, dataIndex, title, inputType, record, index,...restProps} = this.props;
return (
<EditableContext.Consumer>
{form => {
const { getFieldDecorator } = form;
return (
<td {...restProps}>
{editing ?
this.getInput(record, dataIndex, title, getFieldDecorator)
: restProps.children}
</td>
);
}}
</EditableContext.Consumer>
);
}
}
export default EditableTableCell;
This is my EditableTableCell component
import React, {Component} from 'react';
import { Form} from '#ant-design/compatible';
export const EditableContext = React.createContext();
class EditableTableRow extends Component {
render() {
return (
<EditableContext.Provider value={this.props.form}>
<tr {...this.props} />
</EditableContext.Provider>
);
}
}
export default EditableTableRow=Form.create()(EditableTableRow);
This is my ProductsPage component im having bug in
import React, {Component} from 'react';
import {Button, Layout, notification, Popconfirm, Space, Table,Typography} from "antd";
import {Link} from "react-router-dom";
import {Content} from "antd/es/layout/layout";
import EditableTableRow, {EditableContext} from "../components/EditableTableRow";
import EditableTableCell from "../components/EditableTableCell";
import API from "../server-apis/api";
import {employeesDataColumns} from "../tableColumnsData/employeesDataColumns";
import {CheckCircleFilled, InfoCircleFilled} from "#ant-design/icons";
class ProductsPage extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
error: null,
isLoaded: false,
editingKey: "",
errorMessage: "",
}
}
columns = [
...employeesDataColumns,
{
title: "Actions",
dataIndex: "actions",
width: "10%",
render: (text, record) => {
const editable = this.isEditing(record);
return editable ? (
<span>
<EditableContext.Consumer>
{form => (<a onClick={() => this.saveData(form, record.username)} style={{ marginRight: 8 }}>Save</a>)}
</EditableContext.Consumer>
<a onClick={this.cancel}>Cancel</a>
</span>
) : (
<Space size="middle">
<a onClick={() => this.edit(record.username)}>Edit</a>
<Popconfirm title="Are you sure you want to delete this product?"
onConfirm={() => this.remove(record.username)}>
<a style={{color:"red"}}>Delete</a>
</Popconfirm>
</Space>
);
},
}
];
isEditing = (record) => {
return record.username === this.state.editingKey;
};
edit(username) {
this.setState({editingKey:username});
}
cancel = () => {
this.setState({ editingKey: ""});
};
componentDidMount() {
this.setState({ loading: true });
const token="Bearer "+ JSON.parse(localStorage.getItem("token"));
API.get(`users/all`,{ headers: { Authorization: token}})
.then(res => {
// console.log(res.data._embedded.productList);
const employees = res.data._embedded.employeeInfoDtoList;
this.setState({loading: false,data:employees });
})
}
async remove(username) {
const token="Bearer "+ JSON.parse(localStorage.getItem("token"));
API.delete(`/users/${username}`,{ headers: { Authorization: token}})
.then(() => {
let updatedProducts = [...this.state.data].filter(i => i.username !== username);
this.setState({data: updatedProducts});
this.successfullyAdded("Employee is deleted. It wont have any access to the website anymore.")
}).catch(()=>this.errorHappend("Failed to delete"));
}
hasWhiteSpace(s) {
return /\s/g.test(s);
}
saveData(form,username) {
form.validateFields((error, row) => {
if (error) {
return;
}
const newData = [...this.state.data];
const index = newData.findIndex(item => username === item.username);
const item = newData[index];
newData.splice(index, 1, {
...item,
...row
});
const token="Bearer "+ JSON.parse(localStorage.getItem("token"));
const response = API.put(`/users/${username}/update`, row,{ headers: { Authorization: token}})
.then((response) => {
this.setState({ data: newData, editingKey: ""});
this.successfullyAdded("Empolyee info is updated")
})
.catch(error => {
this.setState({ errorMessage: error.message });
this.errorHappend("Failed to save changes.")
console.error('There was an error!', error);
});
});
}
successfullyAdded = (message) => {
notification.info({
message: `Notification`,
description:message,
placement:"bottomRight",
icon: <CheckCircleFilled style={{ color: '#0AC035' }} />
});
};
errorHappend = (error) => {
notification.info({
message: `Notification`,
description:
`There was an error! ${error}`,
placement:"bottomRight",
icon: <InfoCircleFilled style={{ color: '#f53333' }} />
});
};
render() {
const components = {
body: {
row: EditableTableRow,
cell: EditableTableCell
}
};
const columns = this.columns.map(col => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: record => {
const checkInput = index => {
switch (index) {
case "price":
return "number";
default:
return "text";
}
};
return {
record,
// inputType: col.dataIndex === "age" ? "number" : "text",
inputType: checkInput(col.dataIndex),
dataIndex: col.dataIndex,
title: col.title,
editing: this.isEditing(record)
};
}
};
});
const { data, loading } = this.state;
return (
<Layout>
<div>
<Link to="/add-product">
<Button style={{float:"right", background: "#0AC035",marginBottom:"1em", marginTop:"1em" }}
type="primary">New emplyee</Button>
</Link>
</div>
<Content>
<Table components={components} bordered dataSource={data} columns={columns} loading={loading} rowKey={data.username} rowClassName="editable-row"/>
</Content>
</Layout>
);
}
}
export default ProductsPage;
This is the bug I'm having:
enter image description here
And i want to have this result like its shown in Antd docs:
enter image description here
Id really appreciate if you take a look and help me figure out where im wrong
Updated Solution:
I find the issue. In render where you map the columns, you just return the column if it's not an editable column. You can check the code below. I added a check if it's dataIndex === 'actions', then return the following code:
Please Follow the link:
https://react-ts-v3fbst.stackblitz.io
Changes:
1.In columns, i remove the render function from the action object:
{
title: 'Actions',
dataIndex: 'actions',
width: '10%',
},
2. In render function where you map the columns, add the following code before this condition if(!col.editable) {,:
if (col.dataIndex === 'actions') {
return {
...col,
render: (text, record) => {
const editable = this.isEditing(record);
return editable ? (
<span>
<EditableContext.Consumer>
{(form) => (
<a onClick={() => this.saveData(form, record.username)} style={{ marginRight: 8 }}>
Save
</a>
)}
</EditableContext.Consumer>
<a onClick={this.cancel}>Cancel</a>
</span>
) : (
<Space size='middle'>
<a onClick={() => this.edit(record.username)}>Edit</a>
<Popconfirm title='Are you sure you want to delete this product?' onConfirm={() => this.remove(record.username)}>
<a style={{ color: 'red' }}>Delete</a>
</Popconfirm>
</Space>
);
}
};
}
When you click on edit, you set the username as key for that particular row for editing, make sure you have username in each record. I tested this using the following data:
const data = [
{ id: 8, name: 'baun', model: '2022', color: 'black', price: 358, quantity: 3, username: 'brvim' },
{ id: 3, name: 'galileo', model: '20221', color: 'white', price: 427, quantity: 7, username: 'john' }
];
Most important, you should select that attribute as key that is unique in all records. As you are using username, i don't know what is your business logic or data looks like, but technically each record can have same username. So you must select something that would always be unique in your complete data.

React inline style with a variable from a function

I am trying to display the product getting the size it should be from a Json database. I am new to react so have tried a few ways and this is what I have been able to do.
I tried making a function (FontSize) that creates a variable (percentage) with the value I want before and then tried calling the function in the render in the tag with the product. I am getting no errors but the size of the paragraph tag is not changing.
This is my component.
import React, { Component } from 'react';
import { Loading } from './LoadingComponent';
const API = 'http://localhost:3000/products';
class Products extends Component {
constructor(props) {
super(props);
this.state = {
products: [],
isLoading: false,
error: null,
};
}
componentDidMount() {
this.setState({ isLoading: true });
fetch(API)
.then(response => {
if (response.ok) {
return response.json();
} else {
throw new Error('Something went wrong ...');
}
})
.then(data => this.setState({ products: data, isLoading: false }))
.catch(error => this.setState({ error, isLoading: false }));
}
FontSize = () => {
const { products } = this.state;
var percentage = products.size + 'px';
return percentage;
}
render() {
const Prods = () => {
return (
<div>
<div className="row">
<button onClick={this.sortPrice}>sort by price lower to higher</button>
<button onClick={this.sortSize}>sort by size small to big</button>
<button onClick={this.sortId}>sort by Id</button>
</div>
{products.map(product =>
<div className="row">
<div className="col-3">
<p> Price: ${(product.price/100).toFixed(2)}</p>
</div>
<div className="col-3">
<p style={{fontSize : this.FontSize()}} > {product.face}</p>
</div>
<div className="col-3">
<p>Date: {product.date} {this.time_ago}</p>
</div>
</div>
)}
<p>"~END OF CATALOG~"</p>
</div>
);
};
const { products, isLoading, error } = this.state;
if (error) {
return <p>{error.message}</p>;
}
if (isLoading) {
return <Loading />;
}
return (
<Prods />
);
}
}
export default Products;
What I get in the console using console.log(products)
I think you need quotes around your style value to work properly.
With concatenation it would look like this for Example:
style={{gridTemplateRows: "repeat(" + artist.gallery_images.length + ", 100px)"}}
Another general example from React:
const divStyle = {
color: 'blue',
backgroundImage: 'url(' + imgUrl + ')',
};
function HelloWorldComponent() {
return <div style={divStyle}>Hello World!</div>;
}

How to use posts :title in url instead of :id in React.js

I've done a tutorial on setting up dynamic nested routes using posts as an example. However, the example only showed how to use :id in the url, I want to be able to use :title from my database instead.
So right now I am able to get this url when I click on an item from the list of posts from /posts: '/posts/1' which comes from '/posts/:id'.
However, I want to be able to show '/posts/ramen' in the url when the ramen post is selected. I tried changing the 'id' to 'title' for the 'this.props.history.push' but this seems to affect the way that FullPost.js uses that id to pick up the correct path for the data retrieval via Axios.
Below is my code:
My data is set up like this:
posts.json
posts:
0:
title: "ramen"
id: 1
1:
title: "udon"
id: 2
2:
title: "sushi"
id: 3`
Posts.js: This is a list of posts, where each post is selectable
import React, { Component } from 'react';
import axios from '../../../axiosPosts';
import Aux from '../../../hoc/Aux/Aux';
import classes from './Posts.css';
import Post from '../../../components/Post/Post';
class Posts extends Component {
state = {
posts: []
}
componentDidMount () {
this.getData(this.props.pathname, this.props.filter);
}
getData(pathname, filter) {
axios.get(pathname + '.json')
.then(response => {
const post = response.data.filter(({category}) => category === filter);
const updatedPosts = post.map(post => {
return {
...post
}
});
this.setState({
posts: updatedPosts
});
})
.catch(error => {
console.log(error);
});
}
postSelectedHandler = ( id ) => {
this.props.history.push( this.props.match.url + '/' + id );
}
render () {
let posts = <p style={{textAlign: 'center'}}>Whoops! Something went wrong.</p>;
if(!this.state.error) {
posts = this.state.posts.map(post => {
return (
<Post
key={post.id}
title={post.title}
clicked={() => this.postSelectedHandler( post.id )} />
);
});
};
return (
<Aux>
<div className={classes.PostList}>
<h2 className={classes.PostListTitle}>{this.props.filter}</h2>
{posts}
</div>
</Aux>
)
}
}
export default Posts;
FullPost.js - This is the page that loads up when a post is selected
import React, { Component } from 'react';
import axios from '../../../axiosPosts';
import classes from './FullPost.css';
class FullPost extends Component {
state = {
loadedPost: null
}
componentDidMount () {
this.loadData();
}
loadData() {
if ( this.props.match.params.id ) {
if ( !this.state.loadedPost || (this.state.loadedPost && this.state.loadedPost.id !== +this.props.match.params.id) ) {
axios.get( '/posts/' + (this.props.match.params.id - 1) + '.json' )
.then( response => {
this.setState( { loadedPost: response.data } );
} );
}
}
}
render () {
let post = <p style={{ textAlign: 'center' }}>Please select a Post!</p>;
if ( this.props.match.params.id ) {
post = <p style={{ textAlign: 'center' }}>Loading...!</p>;
}
if ( this.state.loadedPost ) {
post = (
<div className={classes.FullPost}>
Content
</div>
);
}
return post;
}
}
export default FullPost;
You can just pass the 'title' of the post to the 'postSelectedHandler' function instead of 'id' of the post in your Posts.js.
<Post
key={post.id}
title={post.title}
clicked={() => this.postSelectedHandler( post.title, post.id )}
/>
And your 'postSelectedHandler' function will be like:
postSelectedHandler = ( title ) => {
const URL = `${this.props.match.url}/${title}`;
this.props.history.push({pathname: URL, id: id });
}
Access this id in the FullPost.js as:
const { location: {id} } = this.props;
In your 'routes' you can change the '/posts/:id' route with '/posts/:title' route.
This route change is only for your significance so that if someone else sees your code they will understand it easily that you are using the title of the post as route parameter.

Categories