I'm having troubles inserting data to MySQL from Express API Server, I'm getting the data from my React Client. I have 4 fields and one of them is not inserting correctly, you can see it in the below screen (the last 3 rows).
This is my client (React) code.
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {datos: {}};
this.onSubmit = this.handleSubmit.bind(this);
}
handleSubmit(e){
e.preventDefault();
var self = this;
// On submit of the form, send a POST request with the data to the server.
fetch('/todo/meterla',{
method: 'POST',
data:{
task: self.refs.task
}
})
.then(function(response){
return response.json()
}).then(function(body){
console.log(body);
});
}
render() {
return (
<div className="App">
<div className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h2>Welcome to React</h2>
</div>
<form onSubmit={this.onSubmit}>
<input type="text" placeholder="TASK" ref="task"/>
<input type="submit"/>
</form>
</div>
);
}
}
export default App;
This is mi server code:
// Add a new todo
app.post('/todo/meterla', function (req, res) {
var task = req.body;
var query = mc.query("INSERT INTO tasks SET ? ", { task: task }, function (error, results, fields) {
});
});
In the client code, change data to body in the onSubmit handler:
fetch('/todo/meterla',{
method: 'POST',
body:{
task: self.refs.task
}
})
In the server code, change this line var task = req.body; to var task = req.body.task;.
Related
I am new to Shopify App development and I try to implement routing inside my embedded Shopify App.
I have setup the ClientRouter and also integrated it inside the app.js (see below). When I set Navigation Links through the partners Account, the navigation menu appears and the links and the redirecting work as well.
As soon as I try to navigate the user to a page on a Button click, I get the error:
Expected a valid shop query parameter
I am trying to navigate the user by just giving the path for the page:
<Button url="/users">Users</Button>
My other files are listed below:
index.js
import { Page, Heading, Button } from "#shopify/polaris";
const Index = () => (
<Page>
<Heading>Index PAGE</Heading>
<Button url="/users"> Users </Button>
</Page>
);
export default Index;
app.js
import ApolloClient from "apollo-boost";
import { ApolloProvider } from "react-apollo";
import App from "next/app";
import { AppProvider } from "#shopify/polaris";
import { Provider, useAppBridge } from "#shopify/app-bridge-react";
import { authenticatedFetch } from "#shopify/app-bridge-utils";
import { Redirect } from "#shopify/app-bridge/actions";
import "#shopify/polaris/dist/styles.css";
import translations from "#shopify/polaris/locales/en.json";
import ClientRouter from "../components/ClientRouter";
function userLoggedInFetch(app) {
const fetchFunction = authenticatedFetch(app);
return async (uri, options) => {
const response = await fetchFunction(uri, options);
if (
response.headers.get("X-Shopify-API-Request-Failure-Reauthorize") === "1"
) {
const authUrlHeader = response.headers.get(
"X-Shopify-API-Request-Failure-Reauthorize-Url"
);
const redirect = Redirect.create(app);
redirect.dispatch(Redirect.Action.APP, authUrlHeader || `/auth`);
return null;
}
return response;
};
}
function MyProvider(props) {
const app = useAppBridge();
const client = new ApolloClient({
fetch: userLoggedInFetch(app),
fetchOptions: {
credentials: "include",
},
});
const Component = props.Component;
return (
<ApolloProvider client={client}>
<Component {...props} />
</ApolloProvider>
);
}
class MyApp extends App {
render() {
const { Component, pageProps, host } = this.props;
return (
<AppProvider i18n={translations}>
<Provider
config={{
apiKey: API_KEY,
host: host,
forceRedirect: true,
}}
>
<ClientRouter />
<MyProvider Component={Component} {...pageProps} />
</Provider>
</AppProvider>
);
}
}
MyApp.getInitialProps = async ({ ctx }) => {
return {
host: ctx.query.host,
API_KEY: process.env.SHOPIFY_API_KEY,
};
};
export default MyApp;
ClientRouter.js
import { withRouter } from "next/router";
import { ClientRouter as AppBridgeClientRouter } from "#shopify/app-bridge-react";
function ClientRouter(props) {
const { router } = props;
return <AppBridgeClientRouter history={router} />;
}
export default withRouter(ClientRouter);
I am really looking forward to someone who can help me out! Thanks in advance!
Getting undefined data type error while fetching data from JSON
I have searched at many places but didn't get the suitable answer
import SavedData from "./SavedData";
export default class Saved extends React.Component {
constructor() {
super();
this.state = {
loading: true,
datas: [],
};
}
async componentDidMount() {
const url = "https://todo-list-site.herokuapp.com/todo-data";
const response = await fetch(url);
const todoData = response.json().then((res) => {
this.setState({ datas: res });
});
}
render() {
console.log(this.state.datas[0].description); //not able to get data
return (
<div>
{/* {this.state.datas.map((items) => (
<SavedData
key={items.countTodo}
title={items.title}
desc={items.desc}
/>
))} */}
</div>
);
}
}
Someone help me so that I can proceed
Just like Dave Newton has pointed out in the comments, the render is triggered before the request completes. This is normal and you just need to handle it properly.
If you see the console logs of this codesandbox, you can see that initially this.state.datas is just an empty array [] - so any attempt to access this.state.datas[0].description will be undefined. Only after the state is updated when the request completes, the logs show the data retrieved - this is because according to the mount lifecycle of a React Component, the render() is called before the componentDidMount() and also the request being async.
This is very common and it is even recommended by the official React docs to make HTTP calls in componentDidMount(). The docs also has provided an example to handle this issue.
import SavedData from "./SavedData";
export default class Saved extends React.Component {
constructor() {
super();
this.state = {
loading: true, // we initially set this to true
datas: [],
};
}
async componentDidMount() {
const url = "https://todo-list-site.herokuapp.com/todo-data";
const response = await fetch(url);
const todoData = response.json().then((res) => {
this.setState({
datas: res,
loading: false // when the request is complete, we set this to false
});
});
}
render() {
if (this.state.loading) {
// during the first render, loading will be true and we
// can return a loading message or a spinner
return (
<div>Loading...</div>
);
}
// when render is called after the state update, loading will be false
// and this.state.datas will have the fetched data
console.log(this.state.datas[0].description);
return (
<div>
{this.state.datas.map((items) => (
<SavedData
key={items.countTodo}
title={items.title}
desc={items.desc}
/>
))}
</div>
);
}
}
Your datas state is initially an empty array until your componentDidMount fires and sets the state. As a result, your console log will then be undefined until the state is set. In order to combat this you must wait for this.state.datas[0] to be true before accessing the first objects description within the array. The following code seems to work as expected
import React from "react";
export default class Saved extends React.Component {
constructor() {
super();
this.state = {
loading: true,
datas: []
};
}
async componentDidMount() {
const url = "https://todo-list-site.herokuapp.com/todo-data";
const response = await fetch(url);
response.json().then((res) => {
this.setState({ datas: res });
});
}
render() {
console.log(this.state.datas[0] && this.state.datas[0].description);
return (
<div>
{this.state.datas.map((items, i) => (
<div key={i}>
<div> title={items.title}</div>
<div> desc={items.description}</div>
</div>
))}
</div>
);
}
}
Im trying to put a value that I get from axios on a div while I export that function
import React from 'react';
import axios from 'axios';
function callServer() {
axios.get(`http://localhost:${process.env.REACT_APP_SERVER_PORT}`, {
params: {
table: 'querotable',
},
}).then((response) => {
const resp = response.data;
console.log(resp);
return <div>{JSON.stringify(resp)}</div>;
});
}
export function SampleComponent() {
return (
<div>
{callServer()}
</div>
);
}
It shows nothing on div, only on console with the value that I want
Missing return
Your callServer function doesn't return anything. It doesn't even return a Promise. The statement return <div>{JSON.stringify(resp)}</div> is the return for the .then callback -- not for the function itself.
Use Component State
We could return a Promise that resolves to a div but that wouldn't be right. In order to handle asynchronous data in React we want to store that data to state.
const [resp, setResp] = useState();
Side Effects Go in useEffect
We also need to make sure that the axios.get function is only called once instead of on every re-render of SampleComponent. We can do that with a useEffect hook with an empty dependency array.
export function SampleComponent() {
const [resp, setResp] = useState();
useEffect(() => {
axios
.get(`http://localhost:${process.env.REACT_APP_SERVER_PORT}`, {
params: {
table: "querotable"
}
})
.then((response) => setResp(response.data));
}, []);
return (
<div>
<div>{JSON.stringify(resp)}</div>
</div>
);
}
I am trying to upload a photo in my React application, along with some form data. It works with uploading form data from ItemAdd.jsx, a child component of ItemList.jsx. However, when I try to also POST an image file with this data, the image property is undefined when it hits the server.
My suspicion is that I'm using the wrong content-type in the request, but I'm not sure what I should be using instead (if that is the issue here).
Parent Component - ItemList.jsx
import React from 'react';
import 'whatwg-fetch';
import classNames from 'classnames';
import ItemAdd from './ItemAdd.jsx';
export default class ItemList extends React.Component {
constructor() {
super();
this.createItem = this.createItem.bind(this);
}
createItem(newItem) {
console.log('PHOTO:', newItem.image);
fetch('/api/item', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newItem),
}).then(response => {
}).catch(err => {
});
}
render() {
return (
<div>
<ItemAdd createItem={this.createItem} />
</div>
);
}
}
Child Component - ItemAdd.jsx
import React from 'react';
export default class ItemAdd extends React.Component {
constructor() {
super();
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {
image: null,
imagePreviewUrl: null
}
}
handleSubmit(e) {
e.preventDefault();
let form = document.forms.itemAdd;
this.props.createItem({
name: form.name.value,
image: this.state.image
});
// Clear the form and state for the next input.
form.name.value = "";
this.state.image = null;
this.state.imagePreviewUrl = null;
}
handleImageChange(e) {
e.preventDefault();
let reader = new FileReader();
let file = e.target.files[0];
reader.onloadend = () => {
this.setState({
image: file,
imagePreviewUrl: reader.result
});
}
reader.readAsDataURL(file)
}
render() {
let { imagePreviewUrl } = this.state;
let $imagePreview = null;
if (imagePreviewUrl) {
$imagePreview = (<img src={imagePreviewUrl} className={'img-preview'} />);
} else {
$imagePreview = (<div className="previewText">Please select an image.</div>);
}
return (
<div>
<form name="itemAdd" onSubmit={this.handleSubmit}>
<table>
<tr>
<td><label for="name">Name:</label></td>
<td><input type="text" name="name" id="name" placeholder="Name" /></td>
</tr>
<tr>
<td><input type="file" onChange={(e) => this.handleImageChange(e)} /></td>
<td>
<div className="img-preview">
{$imagePreview}
</div>
</td>
</tr>
<tr>
<td><button>Add</button></td>
</tr>
</table>
</form>
</div>
);
}
}
You might not be able to post an image as part of JSON data, calling JSON.stringify() on an image is not a good idea.
I would recommend using formData to submit the form, which makes it multipart/form-data content type.
You might have to handle that differently in the backend.
Example :
createItem(newItem) {
console.log('PHOTO:', newItem.image);
const h = {}; //headers
let data = new FormData();
data.append('image', newItem.image);
data.append('name', newItem.name);
h.Accept = 'application/json'; //if you expect JSON response
fetch('/api/item', {
method: 'POST',
headers: h,
body: data
}).then(response => {
// TODO : Do something
}).catch(err => {
// TODO : Do something
});
}
You can read more on formData
(as you can see my reputation is not very high :) and I understand that if you don't like my question it is going to be my last one, therefore I am going to write it as good as I can :)
The problem I am facing is a similar to:
Redux loses state when navigating to another page
However, the answer to the above question was to use 'history.push', which is what I am doing, and I am still having a problem.
I am using:
"react": "^16.0.0"
"react-redux": "^5.0.6"
"react-router": "^4.2.0"
"react-router-dom": "^4.2.2"
"redux": "^3.7.2"
"redux-promise":"^0.5.3"
"axios": "^0.17.1"
I am doing the following:
In a react component, "SearchText", getting a text string and calling an action creator
In the action creator, using the text string to send an HTTP request to goodreads.com
In my reducer, using the action payload to set the redux state
Using another component, "BookResults" (in another route), to display this state
The component "SearchText" has a link to the "BookResults" page.
So, once "SearchText" fires the action creator, if (when I see on the console that a result is received and the state is set with a list of books) I click on the link that routes to "BookResults", I see the list of books.
If, however, "SearchText" uses (when firing the action creator) a callback that performs history.push of the new page, and this callback is called by 'axios(xxx).then', the state is not set properly, although I see in the console that the HTTP request was successful.
I am sure you can see what I am doing wrong (and I hope it is not very stupid)... Please tell me.
Here is the code:
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { createStore, applyMiddleware } from 'redux';
import ReduxPromise from 'redux-promise';
import SearchText from './components/search_text';
import BookResults from './components/book_results';
import reducers from './reducers';
const createStoreWithMiddleware = applyMiddleware(ReduxPromise)(createStore);
ReactDOM.render(
<Provider store={createStoreWithMiddleware(reducers)}>
<BrowserRouter>
<div>
<BrowserRouter>
<Switch>
<Route path="/book_results" component={BookResults} />
<Route path="/" component={SearchText} />
</Switch>
</BrowserRouter>
</div>
</BrowserRouter>
</Provider>
, document.querySelector('#root'));
SearchText component
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { Link } from 'react-router-dom';
import { searchForBooks } from '../actions';
class SearchText extends Component {
constructor(props) {
super(props);
this.state = {
searchText: ''
};
this.handleFormSubmit = this.handleFormSubmit.bind(this);
this.handleSearchTextChange = this.handleSearchTextChange.bind(this);
}
handleSearchTextChange(e) {
this.setState({ searchText: e.target.value });
}
handleFormSubmit(e) {
e.preventDefault();
const formPayload = {
searchText: this.state.searchText
};
console.log("In SearchBooks/handleFormSubmit. Submitting. state: ", this.state);
this.props.searchForBooks(formPayload, () => {
this.props.history.push(`/book_results`);
});
}
render() {
return (
<form className="container" onSubmit={this.handleFormSubmit}>
<h3>Search Form</h3>
<div className="form-group">
<label className="form-label">{'Search Text:'}</label>
<input
className='form-input'
type='text'
name='searchText'
value={this.state.searchText}
onChange={this.handleSearchTextChange}
onBlur={this.handleSearchTextBlur}
placeholder='' />
</div>
<br />
<input
type="submit"
className="btn btn-primary float-right"
value="Submit"/>
<br /><br />
<Link to={`/book_results`}>⇐ Book Results</Link>
</form>
);
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ searchForBooks: searchForBooks }, dispatch);
}
export default connect(null, mapDispatchToProps)(SearchText);
BookResults component
import React from 'react';
import { connect } from 'react-redux';
import _ from 'lodash';
import Book from './book';
class BookResults extends React.Component {
render() {
let books;
const booksArray = _.values(this.props.bookResults);
console.log("***In BookResults. booksArray: ", booksArray);
if (booksArray.length === 0) {
books = "No books to display";
} else {
books = booksArray.map( (book) => {
return (
<Book book={book} key={book.id} />
);
});
}
return (
<div>
<h2>Search Results</h2>
<br />
<ul>
{books}
</ul>
</div>
);
}
}
function mapStateToProps(state) {
return {
bookResults: state.bookResults,
cats: state.cats
};
}
export default connect(mapStateToProps)(BookResults);
Book component
import React from 'react';
const Book = (props) => (
<li>
{props.book.title}
</li>
);
export default Book;
actions/index.js
As you can see below, the following line is commented out:
// .then(() => callback());
If I include it, I have the problem.
import axios from 'axios';
export const SEARCH_FOR_BOOKS = 'search_for_books';
const GOODREADS = "https://www.goodreads.com/search/index.xml";
const KEY = "xxx";
export function searchForBooks(values, callback) {
let result;
console.log("In actions/searchForBooks. values: ", values);
if (!values.searchText || values.searchText === "") {
console.error("*** ERROR *** In actions/searchForBooks." +
"values.searchText: ", values.searchText);
} else {
const searchUrl = `${GOODREADS}?key=${KEY}&q=${values.searchText}`;
console.log("In actions/searchForBooks. url: " + searchUrl);
result = axios.get(searchUrl);
// .then(() => callback());
}
return {
type: SEARCH_FOR_BOOKS,
payload: result
};
}
reducers/index.js
import { combineReducers } from 'redux';
import bookResultsReducer from './reducer_book_results';
const rootReducer = combineReducers({
bookResults: bookResultsReducer
});
export default rootReducer;
The reducer
import { parseString } from 'xml2js';
import _ from 'lodash';
import { SEARCH_FOR_BOOKS } from '../actions/index';
const bookResults = {};
export default function bookResultsReducer(state = bookResults, action) {
switch (action.type) {
case SEARCH_FOR_BOOKS:
console.log("In bookResultsReducer. payload: ", action.payload);
if (action.error) { // error from goodreads search books
console.error("*** APP ERROR *** In bookResultsReducer. action.error: ", action.error);
} else if (!action.payload || !action.payload.data) {
console.error("*** APP ERROR *** In bookResultsReducer." +
" action.payload or action.payload.data is undefined", action.payload);
} else {
parseString(action.payload.data, function(err, result) {
if (err) {
console.error("*** APP ERROR *** In bookResultsReducer. Error from parseString: ", err);
} else {
state = Object.assign({}, getBooks(result));
}
});
}
console.log("In bookResultsReducer. new state: ", state);
return state;
break;
default:
return state;
}
}
function getBooks(data) {
const bookResults = data.GoodreadsResponse.search[0].results[0].work;
if (!bookResults || bookResults.length === 0) {
return {};
} else {
const results = bookResults.map( (book, index) => {
const bookInfo = book.best_book[0];
return (
{ id: index + 1,
title: bookInfo.title[0] }
);
});
return _.mapKeys(results, 'id');
}
}
Someone sent me the solution by mail.
The error was in the actions/index.js file.
Instead of:
import axios from 'axios';
export const SEARCH_FOR_BOOKS = 'search_for_books';
const GOODREADS = "https://www.goodreads.com/search/index.xml";
const KEY = "xxx";
export function searchForBooks(values, callback) {
let result;
console.log("In actions/searchForBooks. values: ", values);
if (!values.searchText || values.searchText === "") {
console.error("*** ERROR *** In actions/searchForBooks." +
"values.searchText: ", values.searchText);
} else {
const searchUrl = `${GOODREADS}?key=${KEY}&q=${values.searchText}`;
console.log("In actions/searchForBooks. url: " + searchUrl);
result = axios.get(searchUrl)
.then(() => callback());
}
return {
type: SEARCH_FOR_BOOKS,
payload: result
};
}
I should have written:
import axios from 'axios';
export const SEARCH_FOR_BOOKS = 'search_for_books';
const GOODREADS = "https://www.goodreads.com/search/index.xml";
const KEY = "xxx";
export function searchForBooks(values, callback) {
let result;
console.log("In actions/searchForBooks. values: ", values);
if (!values.searchText || values.searchText === "") {
console.error("*** ERROR *** In actions/searchForBooks." +
"values.searchText: ", values.searchText);
} else {
const searchUrl = `${GOODREADS}?key=${KEY}&q=${values.searchText}`;
console.log("In actions/searchForBooks. url: " + searchUrl);
result = axios.get(searchUrl)
.then((res) => {
callback();
return res;
});
}
return {
type: SEARCH_FOR_BOOKS,
payload: result
};
}
Explanation:
The issue is that the returned value from axios.get is passed to the .then clause, and whatever is returned from the .then clause is set to be the value of result.
My error was that I didn't return anything from the .then clause, and therefore the value of result was undefined, and not the returned promise.