This is hard to explain without uploading my full project likely, but here goes. I think I've narrowed it down to some combination of getInitialProps() and getStaticProps(). When I use next/link to change pages images are not being loaded. If I browse directly to the page images will load fine. Project is fairly simple with only 2 pages, index.js and [slug].js. Here's both:
index.js
import React from 'react';
import Layout from '../components/layout';
import Seo from '../components/seo';
import Hero from '../components/hero';
import Forcast from '../components/forcast';
import { fetchAPI } from '../lib/api';
import ReactMarkdown from 'react-markdown';
const Home = ({ pages, homepage }) => {
return (
<Layout pages={pages}>
<Seo seo={homepage.seo} />
<Hero hero={homepage.hero} />
<Forcast />
<main className='main-content'>
<div className='fullwidth-block'>
<div className='container'>
<div className='post single'>
<div className='entry-content'>
<ReactMarkdown
source={homepage.Content}
escapeHtml={false}
transformImageUri={uri =>
uri.startsWith('http') ? uri : `${process.env.REACT_APP_IMAGE_BASE_URL}/${uri}`
}
/>
</div>
</div>
</div>
</div>
</main>
</Layout>
);
};
export async function getStaticProps() {
// Run API calls in parallel
const [pages, homepage] = await Promise.all([
fetchAPI('/pages'),
fetchAPI('/homepage'),
]);
return {
props: { pages, homepage },
revalidate: 1,
};
}
export default Home;
[slug].js
import ReactMarkdown from 'react-markdown';
import Layout from '../components/layout';
import Seo from '../components/seo';
import { fetchAPI } from '../lib/api';
const Page = ({ page, pages }) => {
const seo = {
metaTitle: page.Title,
metaDescription: page.seo.metaDescription,
shareImage: page.seo.shareImage,
}
return (
<Layout pages={pages}>
<Seo seo={page.seo} />
<main className='main-content'>
<div className='container'>
<div className='breadcrumb'>
</div>
</div>
<div className='fullwidth-block'>
<div className='container'>
<div className='row'>
<div className='content col-md-8'>
<div className='post single'>
<h2 className='entry-title'>{page.Title}</h2>
<ReactMarkdown
source={page.Content}
escapeHtml={false}
transformImageUri={uri =>
uri.startsWith('http') ? uri : `${process.env.REACT_APP_IMAGE_BASE_URL}${uri}`
}
/>
</div>
</div>
</div>
</div>
</div>
</main>
</Layout>
);
};
export async function getStaticPaths() {
const pages = await fetchAPI('/pages');
return {
paths: pages.map((page) => ({
params: {
slug: page.slug,
},
})),
fallback: false,
};
}
export async function getStaticProps({ params }) {
const pages = await fetchAPI(
`/pages?slug=${params.slug}`
);
return {
props: { page: pages[0], pages },
revalidate: 1,
};
}
export default Page;
This might also be a Strapi issue though I'm not sure.
The issue happens because the REACT_APP_IMAGE_BASE_URL is not exposed to the browser, and only available on the server.
To have it exposed to the browser you'll need to add the NEXT_PUBLIC_ prefix to it.
# .env.development
NEXT_PUBLIC_REACT_APP_IMAGE_BASE_URL=http://localhost:1337
Then in your code reference it using process.env.NEXT_PUBLIC_REACT_APP_IMAGE_BASE_URL.
Related
I'm building a Dashboard style app that would show data service outages. The backend is Rails 6 and I'm using React/Redux in the frontend (within Rails). I'm having lots of trouble (due to my greenness in Redux) with getting the data into the front end and mapping state to props. Would love for someone to look at my app and see where I'm going wrong. It seems like I'm also having issues with Lexical behaviour as well.
Here is the top of the app:
import React from 'react';
import { render } from 'react-dom'
import Dashboard from './Dashboard';
import { Provider } from "react-redux";
import { createStore, applyMiddleware, compose } from 'redux'; // we get our store from redux library and we need middleware to wire up Thunk
import thunk from 'redux-thunk';
import reducers from './reducers/rootReducer';
import "bootstrap/dist/css/bootstrap.css";
import "bootstrap/dist/css/bootstrap.min.css";
const storeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducers, storeEnhancers(applyMiddleware(thunk)));
// this is how you hook up
store.subscribe(() => {
console.log('the new state is', store.getState());
console.log('----------');
});
render(
<Provider store={store}>
<Dashboard />
</Provider>,
document.body.appendChild(document.createElement('div')),
)
This is the top visible component Dashboard.js
import React, { Component } from "react";
import RecurringOutagesContainer from "./containers/RecurringOutagesContainer";
import FutureOutagesContainer from "./containers/FutureOutagesContainer";
import CurrentOutagesContainer from "./containers/CurrentOutagesContainer";
import CreateModalComponent from "./components/CreateModalComponent";
import { Container, Row, Col, Image } from "react-bootstrap";
import { getFutureOutages } from "./actions/fetchFutureOutagesAction";
import { getRecurringOutages } from "./actions/fetchRecurringOutagesAction";
import { getServices } from "./actions/fetchServicesAction";
import { connect } from 'react-redux';
class Dashboard extends Component {
state = {
services: [],
outages: [],
showModal: false
};
componentDidMount() {
this.props.getFutureOutages()
this.props.getRecurringOutages()
this.props.getServices()
}
render() {
console.log(this.props)
return (
<div>
<Container>
<Row>
<Col sm={1}>
<img
src={require("./public/logo-2-dashboard.png")}
alt="logo"
id="logo"
></img>
</Col>
<Col md={8}></Col>
</Row>
</Container>
<div className="container">
<div className="d-flex justify-content-md-end bd-highlight">
</div>
</div>
<div className="d-flex justify-content-center bd-highlight dashboard">
<div className="d-flex justify-content-start bd-highlight">
<div className="d-fliex pastOutages">
<h4>Past Outages</h4>
</div>
</div>
<div className="d-flex justify-content-center bd-highlight">
<div className="d-fliex currentOutages">
<h4>Current Outages</h4>
<div className="container">
<div className="col-12">
<CurrentOutagesContainer currentOutages={this.props.services} />
</div>
</div>
</div>
</div>
<div className="d-flex align-items-center flex-column bd-highlight">
<div className="d-fliex justify-content-center">
<h4>Future Outages</h4>
<div className="container" id="futureOutages">
<div className="col-12">
<FutureOutagesContainer futureOutages={this.props.futureOutages} />
</div>
</div>
<h4>Recurring Outages</h4>
<div className="container" id="recurringOutages">
<div className="col-12">
<RecurringOutagesContainer recurringOutages={this.props.recurringOutages} />
</div>
</div>
</div>
</div>
</div>
</div>
);
}
}
const mapStateToProps = state => {
return {
futureOutages: state.futureOutages,
recurringOutages: state.recurringOutages,
services: state.services
}
};
const mapDispatchToProps = dispatch => {
return {
getFutureOutages: () => dispatch(getFutureOutages()),
getRecurringOutages: () => dispatch(getRecurringOutages()),
getServices: () => dispatch(getServices())
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Dashboard); // this connects Dashboard to store
Here is one example of an action file:
\\ fetchFutureOutagesAction.js
import axios from 'axios';
export const getFutureOutages = () => dispatch => {
axios.get("/future_outages")
.then(res => {
const futureOutages = res.data;
dispatch({ type: 'FUTURE_OUTAGES', payload: futureOutages });
})
.catch(res => console.log(res.errors));
};
I have a rootReducer like so:
import { combineReducers } from 'redux';
import { futureOutagesReducer } from './futureOutagesReducer';
import { recurringOutagesReducer } from './recurringOutagesReducer';
import { servicesReducer } from './servicesReducer';
export default combineReducers({
futureOutages: futureOutagesReducer,
recurringOutages: recurringOutagesReducer,
services: servicesReducer
});
and here is an example of a reducer file:
const initialState = {
futureOutages: []
}
export const futureOutagesReducer = (state = initialState, action) => {
switch (action.type) {
case 'FUTURE_OUTAGES':
return { futureOutages: [...state.futureOutages, action.payload] };
default:
return state;
}
}
The errors are occuring in the container files that I pass props down to from `Dashboard.jsx':
import React from "react";
import FutureOutagesComponent from "../components/FutureOutagesComponent"
const FutureOutagesContainer = props => {
return (
<div>
{props.futureOutages && props.futureOutages.map((futureOutage, idx) => (
<FutureOutagesComponent key={idx} futureOutage={futureOutage} />
))
}
</div>
)
};
export default FutureOutagesContainer;
When I start ./bin/webpack-dev-server.js, here is a snapshot of errors in console I'm getting:
So clearly the props are not being passed down correctly. Can someone give me some pointers on implementing this better? I had everything working with just a React app but really want to have more flexibility accessing state through out the app.
Based on your reducer, I think you will need to access future outages like this.
const mapStateToProps = state => {
return {
futureOutages: state.futureOutages.futureOutages
}
};
The name of your reducer is futureOutages and it also has a property by the same name whose value is an array.
export default combineReducers({
// state.futureOutages
futureOutages: futureOutagesReducer,
...
})
Accessing state.futureOutages will give you an object which is the full piece of state for that reducer from the Refux store. But you want a specific property. Because it's an object and not array, Array.prototype.map is not a func. HTH.
I have 2 problems regarding the below code. I want to remove specific items from my cart in React. ( backend Rails). I know that the splice approach is one of the most used but in my case, it does not delete the specific clicked one, but it deletes the last item all the time regardless of the one that I click one.
My second problem is that my Total does not get updated even if the items do get deleted in the wrong way but still get deleted.
This is the code:
import React, { Component } from 'react';
import BasketPic from './images/cart.png';
import StripePayment from './stripePayment';
class MainBasket extends React.Component {
constructor(props) {
super(props);
this.state = {
toggle: true,
showBasket: false,
items: this.props.items
}
this.toggleBasket = this.toggleBasket.bind(this)
this.removeItemFromBasket = this.removeItemFromBasket.bind(this)
console.log(this.state.items)
}
toggleBasket(){
this.setState({toggle: !this.state.toggle});
}
showCheckout(){
this.setState({showBasket: !this.state.showBasket})
}
addItemToBasket(itemId){
fetch(`http://localhost:3000/books/${itemId}`)
.then( item => item.json())
.then( item => {
this.state.items.push(item);
this.state.total += item.price;
this.setState(this.state);
})
}
removeItemFromBasket(itemId){
var itemToBeDeleted = this.state.items.indexOf(itemId)
var deleted = this.state.items.splice(itemToBeDeleted, 1)
this.setState(this.state)
}
render(){
var count_items = this.props.items.length
var total = this.props.total
return(
<div className="basketInfo">
<div className="mainBasketDiv">
<img src={BasketPic} onClick={this.toggleBasket} />
<strong><p className="itemsCart">{count_items}</p></strong>
<strong><p className="checkOutConf">CheckOut</p></strong>
<div className={"toggleDiv-" + this.state.toggle}>
<h3>{"Total: " + this.props.total}</h3>
<span><h4>{"Items: " + count_items }</h4></span>
<hr/>
{this.props.items.map( item =>
<div className="animated fadeInRight" key={"item-" + item.id}>
<h2>{item.title}</h2>
<h6 onClick={this.removeItemFromBasket} className="remvProd">{"Remove " + item.title}</h6>
</div>
)}
<button onClick={function(){this.showCheckout()}.bind(this)}> Check out</button>
</div>
</div>
<div className="container">
<div className={"checkOutStripe-" + this.state.showBasket}>
<div className="totalBar">
<p className="totalBarTypography"> Total {this.props.items.length} {this.props.items.length < 2 ? "item" : "items"}</p>
<p className="totalBarTypography"> Total {this.props.total} GBP</p>
</div>
<div className="row">
{this.props.items.map(eachItem =>
<div className="col-sm" key={"eachItem-" + eachItem.id}>
<img src={eachItem.image.url} className="checkOutImg"/>
<div className="prodDetails">
<h3>{"Product: " + eachItem.title }</h3>
<h3>{"Price: " + eachItem.price }</h3>
</div>
</div>
)}
</div>
<div>
<StripePayment
description={'BooksMania'}
amount={total}
/>
</div>
</div>
</div>
</div>
)
}
}
// module.exports = MainBasket;
export default MainBasket;
and this is the code where i set my total and my items initial state:
import React, { Component } from 'react';
import BuyButton from './images/buyButton.jpg';
import MainBasket from './mainBasket';
import CheckOutBasket from './checkOutBasket';
import Reviews from './reviews';
class EachBook extends React.Component {
constructor(props){
super(props);
this.state = {
newReview: [],
items: [],
total: 0
}
}
seeAllReviews(bookId){
fetch(`http://localhost:3000/books/${bookId}/reviews`)
.then( reviews => reviews.json())
.then( reviews => {
this.setState({
bookReviews: reviews
})
})
}
addReview(bookId){
fetch(`http://localhost:3000/books/${bookId}/reviews`,{
method: 'POST',
mode: 'cors',
body: JSON.stringify({comment: this.refs.comment.value}),
headers: new Headers({
'Accept': 'application/json',
'Content-Type': 'application/json',
'X-CSRF-Token' : this.props.parent.props.parent.props.csrf
}),
credentials: 'same-origin'
}).then(response => response.json())
.catch(error => alert("There is something wrong with this request"))
.then( response => {
this.setState({newReview: response})
})
}
addItemToBasket(itemId){
fetch(`http://localhost:3000/books/${itemId}`)
.then( item => item.json())
.then( item => {
this.state.items.push(item);
this.state.total += item.price;
this.setState(this.state);
})
}
render(){
return(
<div>
<MainBasket items={this.state.items} total={this.state.total} parent={this}/>
<div className="container">
{this.props.singleBook.map(indBook =>
<div className="indBook" key={indBook.id}>
<h1>{indBook.title}</h1> <br />
<h2>{"Author: " + indBook.author}</h2> <br />
<h4>{"Genre: " + indBook.genre}</h4>
<h4>{"Price: " + indBook.price}£</h4>
<img src={indBook.image.url} />
<div className="button"><img src={BuyButton} onClick={function(){this.addItemToBasket(indBook.id)}.bind(this)}/></div>
<div className="description">{indBook.description}</div>
<h3>{this.state.newReview.comment}</h3>
<div>
<h4>Leave a new review</h4>
<textarea ref="comment" type="text" placeholder='Tell us your thoughts '></textarea>
<button onClick={ function(){this.addReview(indBook.id)}.bind(this) } >Submit</button>
</div>
</div>
)}
</div>
</div>
)
}
}
export default EachBook;
Many thanks for your help!
You’re biggest issue is that you’re mutating items in state, so state doesn’t cause a re-render. I would consider using filter instead of splice and just setting items from that like so:
removeItemFromBasket(itemId) {
const items = this.stat.items.filter(item => item.id !== itemId)
this.setState({ items })
}
The addItemToBasket should also have that issue. You should not mutate state as JavaScript passes objects by reference. You should be using this.state.total instead of this.props.total in the render method.
Hope that helps.
I was able to get the url in the attachment field but for the redux form it was empty, how is possible to pass the value of the url to the redux form? Below is the code and the screenshot:
<div className="FileUpload">
<Dropzone
onDrop={this.onImageDrop.bind(this)}
multiple={false}
accept="image/*">
<div>Drop an image or click to select a file to upload.</div>
</Dropzone>
</div>
<div className="form-group">
<label htmlFor="attachment">Attachment:</label><br />
<input className="form-control" focus placeholder="attachment" type="text" name="attachment" ref="attachment" value={this.state.uploadedFileCloudinaryUrl} />
{this.state.uploadedFileCloudinaryUrl === '' ? null :
<div>
<p>{this.state.uploadedFile.name}</p>
<img src={this.state.uploadedFileCloudinaryUrl} alt="" />
</div>}
</div>
<div className="ui small image">
<img src={this.props.workrequest.attachment} alt="" />
</div>
the url in the attachemnt field
The first one is using the React Dropzone to get the url but for the Redux Form it was empty. May I know how to do that to get the url inserts at Redux Form here? Thank you
import React from 'React';
import { connect } from 'react-redux';
import { change, reduxForm } from 'redux-form';
import Dropzone from 'react-dropzone';
class UploadForm extends React.Component {
onDrop = (accepted) => {
if (!accepted.length) return;
// start uploading
this.setState({ isUploading: true });
const formData = new FormData();
formData.append('file', accepted[0]);
axios.post('upload', formData).then(
(res) => {
this.props.dispatch(change('uploadForm', 'url', res.url));
this.setState({ isUploading: false });
},
() => {
this.setState({ isUploading: false });
}
);
};
render() {
return (
<form>
<Dropzone onDrop={this.onDrop} />
</form>
);
}
}
export default connect()(
reduxForm({
form: 'uploadForm',
initialValues: {
url: ''
}
})(UploadForm)
);
Please use this.
Actually, you must use Field component from Redux-form.
Other wise, you can change form values using dispatch and change action creator.
import { Field, change } from 'redux-form';
this.props.dispatch(change('uploadForm', 'url', this.state.url));
I've been trying to pass data from my Rails view to the Vue component as described here
Everything works much as expected, but I'm rather stumped as to how to access the data that I'm passing in via props. Not appearing in the Vue developer tools anywhere and I'm not able to find it by fiddling with/inside the Vue object.
Could someone point me in the right direction. I'm fairly green with Vue, so struggling to even know what to search for :/
show.html.erb
<%= javascript_pack_tag 'test_vue' %>
<%= stylesheet_pack_tag 'test_vue' %>
<%= content_tag :div, id: "test", data: {
message: "this wont!",
name: "nor will this!" }.to_json do %>
<% end %>
test.vue
<template>
<div id="app">
<p>{{test}}{{message}}{{name}}</p>
</div>
</template>
<script>
export default {
data: function () {
return {
test: 'This will display',
}
}
}
</script>
<style>
</style>
test_vue.js
import Vue from 'vue'
import Test from './test.vue'
document.addEventListener('DOMContentLoaded', () => {
const node = document.getElementById('test')
const props = JSON.parse(node.getAttribute('data'))
new Vue({
render: h => h(Test, { props })
}).$mount('#test');
})
Looks like all you need to do is declare the properties in your component:
<template>
<div id="app">
<p>{{test}}{{message}}{{name}}</p>
</div>
</template>
<script>
export default {
props: ["message","name"],
data: function () {
return {
test: 'This will display',
}
}
}
</script>
<style>
</style>
This would be the relevant documentation.
A child component needs to explicitly declare the props it expects to
receive using the props option
I am building an web application in which react-router is used. When i hit the url localhost:8080/user it works fine. When i hit localhost:8080/user/login it not works and console show unexpected tokken > what does it means? I could not understand the problem.
One more thing in this line of code when i changed to any other class then also its not working .
Routes.js
import React from 'react';
import UserBase from './UserBase.js';
import Dashboard from './Dashboard.js';
import Login from './Login.js';
// var ReactRouter = require('react-router');
// var Router = ReactRouter.Router;
// var Route = ReactRouter.Route;
import { Router, Route, IndexRoute, Link, IndexLink, browserHistory } from 'react-router'
var Routes = (
<Router history={browserHistory}>
<Route path="/" component={Login}/>
<Route path="user" component={UserBase}>
<IndexRoute component={Dashboard} />
<Route path="login" component={Login}/>
</Route>
</Router>
);
module.exports = Routes;
Login.js
import React from 'react';
class Login extends React.Component{
constructor(){
super();
}
render(){
return (
<div className="login">
<a className="hiddenanchor" id="signup"></a>
<a className="hiddenanchor" id="signin"></a>
<div className="login_wrapper">
<div className="animate form login_form">
<section className="login_content">
<form>
<h1>Login Form</h1>
</form>
</section>
</div>
<div id="register" className="animate form registration_form">
<section className="login_content">
<form>
<h1>Create Account</h1>
</form>
</section>
</div>
</div>
</div>
);
}
}
export default Login;
Routes js is working fine if I remove 'history={browserHistory}' means that if I use ugly url i.e. used with #. If I hit http://localhost:8080/#/user/login?_k=jtorvg is working fine then what will be the issue?
I use node server and express package to serve for every request.
var app = express();
app.use('/', express.static(path.join(__dirname, 'public')));
app.get('*', function(req, res) {
res.sendFile(path.join(__dirname + '/public/index.html'));
});
webpack.config.js
module.exports = {
entry: "./app/components/EntryPoint.js",
output: {
filename:"public/bundle.js"
},
module : {
loaders: [
{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel',
query: {
presets: ['react', 'es2015']
}
}
]
}
};
Yes. I got the answer after struggling of hours a very small mistake. At index page bundle.js script path has to be changed for the url like localhost:8080/user/dashboard.
Just add <script src="/bundle.js" /> instead of <script src="bundle.js" />