I test some eosjs apis.
But, I am stucked because of some problems.
This is my code.
/////////////////////////////////
import * as Eos from 'eosjs'
class EOSService {
constructor() {
this.eos = Eos({
httpEndPoint : 'https://eu1.eosdac.io:443',
chainId: 'aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e906',
})
}
fetchTransactionData(txid, callback) {
this.eos.getTransaction(txid)
.then( (result) => {
callback(result)
})
}
fetchAccountData(accountName, callback) {
this.eos.getAccount(accountName)
.then( (result) => {
callback(result)
})
}
}
export default EOSService;
I call this methods from react component like below.
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import EOSService from 'services/EOSService'
class AccountPage extends Component {
componentDidMount() {
let accountName = this.props.match.params.accountName;
(new EOSService()).fetchAccountData(accountName, (result) => {
console.log(result)
})
}
render() {
return(
<div>AccountPage</div>
);
}
}
AccountPage.propTypes = propTypes;
AccountPage.defaultProps = defaultProps;
export default AccountPage;
But, I encounter error like this.
=> POST http://127.0.0.1:8888/v1/chain/get_account 0 ()
In other words, I want to ask data from BP's httpEndPoint, but eosjs called 127.0.0.1:8888.
How to solve this?
Summary
You have to use httpEndpoint, not httpEndPoint.
Check below:
<html>
<head>
<meta charset="utf-8">
<script src="https://cdn.jsdelivr.net/npm/eosjs#16.0.0/lib/eos.min.js"
integrity="sha512-vNyLnOEb7uFmEtVbLnyZQ9/k4zckM2Vu3jJOKq6XfWEVZG0yKcjDExVN4EQ7e3F+rePWRncMolI2xFi/3qo62A=="
crossorigin="anonymous"></script>
<script>
eos = Eos({
httpEndpoint: 'https://eu1.eosdac.io:443',
chainId: 'aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e906',
verbose: true
})
</script>
</head>
<body>
See console object: Eos
</body>
</html>
If you use httpEndPoint, config will be set by default. Check this eos.js code:
var Eos = function Eos() {
var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
config = Object.assign({}, {
httpEndpoint: 'http://127.0.0.1:8888',
debug: false,
verbose: false,
broadcast: true,
sign: true
}, config);
var defaultLogger = {
log: config.verbose ? console.log : null,
error: console.error
};
config.logger = Object.assign({}, defaultLogger, config.logger);
return createEos(config);
};
Related
I assume when component is rendered multiple times something happens to setinterval,but how can i fix this.
bottom code is for Store that i am using and i don't understand.someone said that i must have useffect outside component but then it gives me error.
Anyways im new to react so i need help ,everyones appriciated.Thanks.
import SmallLogo from '../img/logo.svg';
import StarskyText from '../img/starskyproject.svg';
import './Statement.css'
import { BrowserRouter as Router,Routes,Route,Link } from "react-router-dom";
import { getElementError } from '#testing-library/react';
import react, { useRef , useState, useEffect } from 'react';
import { useDispatch, useSelector } from "react-redux";
import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap';
import { store } from "./appReducer";
function TempText(props) {
return <span className="yellow changetext"> {props.body} </span>;
}
function doUpdate(callback) {
setInterval(callback, 1300);
}
export default function Statement(){
const dispatch = useDispatch();
const textOptions = ["NFT", "CRYPTO", "METAVERSE", "WEB3"];
const tempText = useSelector((state) => state.tempText);
function change() {
let state = store.getState();
const index = state.index;
console.log(index);
console.log(textOptions[index]);
dispatch({
type: "updatetext",
payload: textOptions[index]
});
let newIndex = index + 1 >= textOptions.length ? 0 : index + 1;
dispatch({
type: "updateindex",
payload: newIndex
});
}
useEffect(() => {
doUpdate(change);
}, []);
var [dropdownOpen , Setdrop] = useState(false);
return(
<div>
<Link to="/">
<img className='star-fixed' alt='starlogo' src={SmallLogo}></img>
</Link>
<img className='starsky-fixed' alt='starsky-project' src={StarskyText}></img>
<div className='text-content'>
<span className='statement-text'>WEB3 IS NOT ONLY THE FUTURE.
IT’S THE ONLY FUTURE!</span>
<span className='starsk-link'>starsk.pro</span>
</div>
<div className='text-content-bottom'>
<span className='statement-text-bottom'>CREATE YOUR NEXT
<TempText body={tempText} />
<span className='flex'> PROJECT WITH
<Dropdown className="hover-drop-out" onMouseOver={() => Setdrop(dropdownOpen=true) } onMouseLeave={() => Setdrop(dropdownOpen=false)} isOpen={dropdownOpen} toggle={() => Setdrop(dropdownOpen = !dropdownOpen) }>
<DropdownToggle className='hover-drop'> STRSK.PRO </DropdownToggle>
<DropdownMenu> </DropdownMenu>
</Dropdown> </span>
</span>
</div>
</div>
)
}
import { createStore } from "redux";
const initialState = {
tempText: "NFT",
index: 1
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case "updatetext":
return {
...state,
tempText: action.payload
};
case "updateindex":
return {
...state,
index: action.payload
};
default:
return state;
}
};
export const store = createStore(reducer);
You can clear your timer by calling clearTimeout function with a reference to your timer when your component unmounting.
useEffect(() => {
const timer = setInterval(change, 1300);
// in order to clear your timeout
return () => clearTimeout(timer);
}, [])
I have tried to follow the udemy guide to implement react router. the problem i have is when loading the page directly with an id without going through a list page first. it does not seem to load the records. i want to go to page /commsmatrix/approve/121 and load record 121. i am using react router v4. in mapStateToProps, records is undefined
approve.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchCommsmatrix } from '../../actions/commsmatrices';
import { bindActionCreators } from 'redux';
import FontAwesome from 'react-fontawesome';
class Approve extends Component {
constructor(props) {
super(props);
this.meta = { title: 'Comms Matrix Approval', description: 'Sox approval' };
this.runOnce = false;
this.passMetaBack = this.passMetaBack.bind(this);
this.initConfirm = this.initConfirm.bind(this);
}
componentDidMount() {
this.passMetaBack;
const { id } = this.props.match.params.id;
this.props.fetchCommsmatrix(id);
}
passMetaBack = () => {
this.props.passMetaBack(this.meta);
};
initConfirm(){
this.runOnce = true;
/*this.props.fetchCommsmatrix(121)
.then(function(response){
console.log(response);
let data = response.payload.data;
if(data.header.error){
self.setState({
showError: true,
errorMsg: data.header.message
});
}else{
}
});*/
}
render() {
console.log(this);
if(!this.runOnce && this.props.isReady){
this.initConfirm();
}
const { record } = this.props ;
console.log(record);
let message = <div>Confirming...<i className="fa fa-spinner fa-spin"></i></div>;
return (
<div className="container-fluid">
<div className="row-fluid top-buffer">{message}</div>
</div>
);
}
}
function mapStateToProps({ records }, ownProps) {
console.log(records);
console.log(ownProps);
return { record : records[ownProps.match.params.id] };
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(
{ fetchCommsmatrix },
dispatch
);
}
export default connect(mapStateToProps, { fetchCommsmatrix })(Approve);
here is my actions
import axios from 'axios';
export const FETCH_COMMSMATRIX = 'fetch_commsmatrix';
export function fetchCommsmatrix(id) {
const request = axios.get(`/api/user/comms/matrices/id/`+id+`/format/json?quiet=1`);
return {
type: FETCH_COMMSMATRIX,
payload: request
};
}
export const FETCH_COMMSMATRICES_BY_SERVICE = 'fetch_commsmatrices_by_service';
export function fetchCommsmatricesByService(service_id) {
const request = axios.get(`/api/user/comms/matrices/format/json?quiet=1&service_id=`+service_id);
return {
type: FETCH_COMMSMATRICES_BY_SERVICE,
payload: request
};
}
here is my reducer
import { FETCH_COMMSMATRIX, FETCH_COMMSMATRICES_BY_SERVICE } from '../actions/commsmatrices';
export default function(state = {}, action) {
switch (action.type) {
case FETCH_COMMSMATRIX:
return { ...state, [action.payload.data.body.recordset.record[0].id] : action.payload.data.body.recordset.record[0] };
case FETCH_COMMSMATRICES_BY_SERVICE:
return action.payload.data.body.recordset.record;
default:
return state;
}
}
here is index reducer
import { combineReducers } from 'redux';
import { reducer as formReducer } from 'redux-form';
import ActiveUserReducer from './reducer_active_user';
import CommsmatricesReducer from './reducer_commsmatrices';
import ContentReducer from './reducer_content';
import ContentVideListReducer from './reducer_content_video_list';
import SecurityExemptionsReducer from './reducer_security_exemption';
import ReportsWorkerJobs from './reducer_reports_workerjobs';
import ReportsWorkerJobsCount from './reducer_reports_workerjobs_count';
import ReportsFactsandfigures from './reducer_reports_factsandfigures';
import ReportsFactsandfiguresCount from './reducer_reports_factsandfigures_count';
import ServicesReducer from './reducer_services';
import ServicesEditCheckReducer from './reducer_services_edit_check';
import ServicesAddReducer from './reducer_services_add';
import ServicesRenameReducer from './reducer_services_rename';
import ServicesRemoveReducer from './reducer_services_remove';
import TemplatesReducer from './reducer_templates';
const rootReducer = combineReducers({
form: formReducer,
activeUser: ActiveUserReducer,
commsmatrices: CommsmatricesReducer,
content: ContentReducer,
contentVideoList: ContentVideListReducer,
reportsWorkerJobs: ReportsWorkerJobs,
reportsWorkerJobsCount: ReportsWorkerJobsCount,
securityExemptions: SecurityExemptionsReducer,
reportsFactsAndFigures: ReportsFactsandfigures,
reportsFactsAndFiguresCount: ReportsFactsandfiguresCount,
services: ServicesReducer,
servicesEditCheck: ServicesEditCheckReducer,
servicesAdd: ServicesAddReducer,
servicesRename: ServicesRenameReducer,
servicesRemove: ServicesRemoveReducer,
templatesReducer: TemplatesReducer
});
export default rootReducer;
here is app
import React, { Component } from 'react';
import { Switch, Route, withRouter, Redirect } from 'react-router-dom';
import ReactGA from 'react-ga';
import { connect } from 'react-redux';
import { fetchActiveUser } from './actions/index';
import { bindActionCreators } from 'redux';
import { getHttpRequestJSON } from './components/HTTP.js';
import Header from './components/header';
import Logout from './components/logout';
import SideBar from './components/sidebar';
import HomeContent from './containers/home';
import Ldapuser from './components/ldapuser';
import Admin from './components/admin/admin';
import Services from './components/services/index';
import SecurityExemptionsNew from './components/security/security_exemptions_new';
import WorkerJobs from './components/reports/workerjobs';
import FactsAndFigures from './components/reports/factsandfigures';
import Approve from './components/commsmatrix/approve';
import CommsMatrixTemplates from './components/commsmatrix/templates';
import CommsMatrixTemplate from './components/commsmatrix/template';
ReactGA.initialize('UA-101927425-1');
function fireTracking() {
ReactGA.pageview(window.location.pathname + window.location.search);
}
class App extends Component {
constructor(props) {
super(props);
this.state = {
isGuest: false,
isSupp: false,
priv: [],
loading: true,
version: '',
redirect: false,
title: 'Home',
description: '',
isReady: false
};
}
setRedirect = () => {
this.setState({
redirect: true
});
};
renderRedirect = () => {
//if (this.state.redirect) {
return <Redirect to="/SSOLogon/manual_login.jsp" />;
//}
};
initData = () => {
let self = this;
getHttpRequestJSON(
'/api/user/get/user/method/is/guest/format/json?quiet=1'
)
.then(response => {
let isGuest = response.body.recordset.record.isGuest;
if (isGuest) {
/*$(".logo").trigger('click');
//$("#overlay").show();
$('#modalIntro').modal('toggle');
$("#modalIntro").on("hidden.bs.modal", function () {
$(".logo").trigger('click');
});*/
}
//self.props.isGuest = isGuest;
//self.props.loading = false;
//self.props.version = response.header.version;
self.setState({
loading: false,
version: response.header.version,
isGuest: isGuest
});
})
.catch(error => {
console.log('Failed!', error);
//$('#myModalError .modal-body').html(error);
//$('#myModalError').modal('show');
});
getHttpRequestJSON(
'/api/user/get/user/method/is/supp/format/json?quiet=1'
)
.then(response => {
self.setState({
isSupp: response.body.recordset.record.isSupp
});
})
.catch(error => {
console.log('Failed!', error);
//$('#myModalError .modal-body').html(error);
//$('#myModalError').modal('show');
});
getHttpRequestJSON(
'/api/user/get/user/method/priv/format/json?quiet=1'
)
.then(response => {
self.setState({
priv: response.body.recordset.record
});
})
.catch(error => {
console.log('Failed!', error);
//$('#myModalError .modal-body').html(error);
//$('#myModalError').modal('show');
});
};
componentDidMount() {
let self = this;
this.props.fetchActiveUser()
.then(() => {
self.initData();
})
.then(() => {
self.setState({
isReady : true
});
})
if (this.props.activeUser.name == 'AuthError') {
this.setRedirect();
}
}
passMetaBack = (meta) => {
this.setState({
title: meta.title,
description: meta.description
})
}
render() {
if (this.props.activeUser.name == 'AuthError') {
//console.log('redirect');
this.renderRedirect();
}
return (
<div>
<Header
activeUser={this.props.activeUser}
loading={this.state.loading}
version={this.state.version}
title={this.state.title}
description={this.state.description}
/>
<SideBar isReady={this.state.isReady} />
<main>
<Switch>
<Route
path="/commsmatrix/approve/:id"
component={Approve}
/>
</Switch>
</main>
</div>
);
}
}
//export default App;
function mapStateToProps(state) {
if (state.activeUser.id > 0) {
ReactGA.set({ userId: state.activeUser.id });
}
// Whatever is returned will show up as props
// inside of the component
return {
activeUser: state.activeUser
};
}
// Anything returned from this function will end up as props
// on this container
function mapDispatchToProps(dispatch) {
// Whenever getUser is called, the result should be passed
// to all our reducers
return bindActionCreators({ fetchActiveUser }, dispatch);
}
//Promote component to a container - it needs to know
//about this new dispatch method, fetchActiveUser. Make it available
//as a prop
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(App));
index.js
import './scripts/api';
import React, { Component } from 'react'
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware, combineReducers } from 'redux';
import { BrowserRouter, Route, browserHistory } from 'react-router-dom';
import promise from 'redux-promise';
import App from './App'
import reducers from './reducers';
import 'react-quill/dist/quill.snow.css'; // ES6
require("babel-core/register");
require("babel-polyfill");
const createStoreWithMiddleware = applyMiddleware(promise)(createStore);
ReactDOM.render(
<Provider store={createStoreWithMiddleware(reducers)}>
<BrowserRouter history={browserHistory}>
<App/>
</BrowserRouter>
</Provider>
, document.getElementById('root'));
UPDATE
so I have updated records to match reducer name. seems to work but need a way to handle errors when there are no records
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchCommsmatrix } from '../../actions/commsmatrices';
import { bindActionCreators } from 'redux';
import FontAwesome from 'react-fontawesome';
class Approve extends Component {
constructor(props) {
super(props);
this.meta = { title: 'Comms Matrix Approval', description: 'Sox approval' };
this.runOnce = false;
this.passMetaBack = this.passMetaBack.bind(this);
this.initConfirm = this.initConfirm.bind(this);
}
componentDidMount() {
this.passMetaBack;
const id = this.props.match.params.id;
this.props.fetchCommsmatrix(id);
}
passMetaBack = () => {
this.props.passMetaBack(this.meta);
};
initConfirm(){
this.runOnce = true;
}
render() {
let message = <div>Confirming...<i className="fa fa-spinner fa-spin"></i></div>;
const { commsmatrix } = this.props ;
if(!this.runOnce && this.props.isReady && Object.keys(commsmatrix).length > 0 ){
this.initConfirm();
}
return (
<div className="container-fluid">
<div className="row-fluid top-buffer">{message}</div>
</div>
);
}
}
function mapStateToProps({ commsmatrices }, ownProps) {
return { commsmatrix : commsmatrices[ownProps.match.params.id] };
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(
{ fetchCommsmatrix },
dispatch
);
}
export default connect(mapStateToProps, { fetchCommsmatrix })(Approve);
Can I see your root index.js for completeness sake? I can't use comments unfortunately.
Why do you have const { id } = this.props.match.params.id;
Shouldn't it be justconst { id } = this.props.match.params;
Does your state object even have a records property?
(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.
I am trying to create a react component with imported data from Google API. I can see the code is working in the console.log but when I try to use that code in React render method, I am not getting anything. When I move my function inside the class it comes up as the function not defined. I cannot understand why?
function handleTouchTap() {
console.log('CHIP selected');
authorize();
}
function handleAccounts(response) {
console.log(response.result.username);
var username = response.result.username
console.log(username);
}
function authorize(event) {
var useImmidiate = event ? false : true;
var authData = {
client_id: CLIENT_ID,
scope: SCOPES,
immidiate: useImmidiate
};
gapi.auth.authorize(authData, function (response) {
gapi.client.load('analytics', 'v3').then(function () {
console.log(response);
gapi.client.analytics.management.accounts.list().then(handleAccounts);
});
});
}
class Chips extends React.Component {
render() {
return (
<div style={styles.wrapper}>
<Chip
onTouchTap={handleTouchTap}
style={styles.chip} >
<Avatar icon={<FontIcon className="material-icons">perm_identity</FontIcon>} />
Login
</Chip>
<Chip
style={styles.chip} >
<Avatar icon={<FontIcon className="material-icons">account_circle</FontIcon>} />
{this.username}
</Chip>
</div>
);
}
}
In most cases, when you want to render something that might change, you want to add it to the state. That way when you call setState the component knows it needs to rerender and show the changes.
Here I added the functions as component methods, so that you can call this.setState on the result. Ideally you would probably do this with redux and use actions but this will work as a self contained component.
class Chips extends React.Component {
handleTouchTap = () => {
console.log('CHIP selected');
this.authorize();
}
handleAccounts = (response) => {
var username = response.result.username;
this.setState({
username
});
}
authorize = (event) => {
var useImmidiate = event ? false : true;
var authData = {
client_id: CLIENT_ID,
scope: SCOPES,
immidiate: useImmidiate
};
gapi.auth.authorize(authData, (response) => {
gapi.client.load('analytics', 'v3').then(() => {
console.log(response);
gapi.client.analytics.management.accounts.list()
.then(this.handleAccounts);
});
});
}
render() {
return (
<div style={styles.wrapper}>
<Chip
onTouchTap={this.handleTouchTap}
style={styles.chip}>
<Avatar icon={<FontIcon className="material-icons">perm_identity</FontIcon>} />
Login
</Chip>
<Chip
style={styles.chip} >
<Avatar icon={<FontIcon className="material-icons">account_circle</FontIcon>} />
{this.state.username}
</Chip>
</div>
);
}
}
I'm following this tutorial to write a Google Maps React Component that lazy loads the library. I have implemented ScriptCache from this gist. The problem I have is that the code only shows Loading map... and the map is never rendered. Any obvious thing that I have missed?
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/latest/css/bootstrap.min.css">
<title>App</title>
</head>
<body>
<div id="app"></div>
<script src="/Scripts/dist/bundle.js"></script>
</body>
</html>
index.tsx:
import * as React from "react";
import * as ReactDOM from "react-dom";
import * as ReactRouter from "react-router";
import * as ReactBootstrap from "react-bootstrap";
import Container from "./Google/GoogleMapComponent";
ReactDOM.render(
<div>
<Container google={(window as any).google} />
</div>,
document.getElementById("app")
);
GoogleApi.tsx:
export const GoogleApi = function (opts: any) {
opts = opts || {}
const apiKey: any = opts.apiKey;
const libraries: any = opts.libraries || [];
const client: any = opts.client;
const URL: string = 'https://maps.googleapis.com/maps/api/js';
const googleVersion: string = '3.22';
let script: any = null;
let google: any = (window as any).google = null;
let loading = false;
let channel: any = null;
let language: any = null;
let region: any = null;
let onLoadEvents: any[] = [];
const url = () => {
let url = URL;
let params = {
key: apiKey,
callback: 'CALLBACK_NAME',
libraries: libraries.join(','),
client: client,
v: googleVersion,
channel: channel,
language: language,
region: region
}
let paramStr = Object.keys(params)
.filter(k => !!(params as any)[k])
.map(k => `${k}=${(params as any)[k]}`).join('&');
return `${url}?${paramStr}`;
}
return url();
}
export default GoogleApi
GoogleApiComponent.tsx:
import * as React from "react";
import * as ReactDOM from 'react-dom'
import cache from './ScriptCache'
import GoogleApi from './GoogleApi'
const defaultMapConfig = {}
export const wrapper = (options: any) => (WrappedComponent: any) => {
const apiKey = options.apiKey;
const libraries = options.libraries || ['places'];
class Wrapper extends React.Component<any, any> {
constructor(props: any, context: any) {
super(props, context);
this.state = {
loaded: false,
map: null,
google: null
}
}
scriptCache: any;
map: any;
mapComponent: any
refs: {
[string: string]: any;
map: any;
}
componentDidMount() {
const refs: any = this.refs;
this.scriptCache.google.onLoad((err: any, tag: any) => {
const maps = (window as any).google.maps;
const props = Object.assign({}, this.props, {
loaded: this.state.loaded
});
const mapRef: any = refs.map;
const node = ReactDOM.findDOMNode(mapRef);
let center = new maps.LatLng(this.props.lat, this.props.lng)
let mapConfig = Object.assign({}, defaultMapConfig, {
center, zoom: this.props.zoom
})
this.map = new maps.Map(node, mapConfig);
this.setState({
loaded: true,
map: this.map,
google: (window as any).google
})
});
}
componentWillMount() {
this.scriptCache = cache({
google: GoogleApi({
apiKey: apiKey,
libraries: libraries
})
});
}
render() {
const props = Object.assign({}, this.props, {
loaded: this.state.loaded,
map: this.state.map,
google: this.state.google,
mapComponent: this.refs.map
})
return (
<div>
<WrappedComponent {...props} />
<div ref='map' />
</div>
)
}
}
return Wrapper;
}
export default wrapper;
GoogleMapComponent.tsx:
import * as React from "react";
import * as ReactDOM from 'react-dom'
import GoogleApiComponent from "./GoogleApiComponent";
export class Container extends React.Component<any, any> {
render() {
const style = {
width: '100px',
height: '100px'
}
return (
<div style={style}>
<Map google={this.props.google} />
</div>
)
}
}
export default GoogleApiComponent({
apiKey: 'AIzaSyAyesbQMyKVVbBgKVi2g6VX7mop2z96jBo ' //From Fullstackreact.com
})(Container)
export class Map extends React.Component<any, any> {
refs: {
[string: string]: any;
map: any;
}
map: any;
componentDidMount() {
this.loadMap();
}
componentDidUpdate(prevProps: any, prevState: any) {
if (prevProps.google !== this.props.google) {
this.loadMap();
}
}
loadMap() {
if (this.props && this.props.google) {
// google is available
const {google} = this.props;
const maps = google.maps;
const mapRef = this.refs.map;
const node = ReactDOM.findDOMNode(mapRef);
let zoom = 14;
let lat = 37.774929;
let lng = -122.419416;
const center = new maps.LatLng(lat, lng);
const mapConfig = Object.assign({}, {
center: center,
zoom: zoom
})
this.map = new maps.Map(node, mapConfig);
}
// ...
}
render() {
return (
<div ref='map'>
Loading map...
</div>
)
}
}
ScriptCache.tsx:
let counter = 0;
let scriptMap = new Map();
export const ScriptCache = (function (global: any) {
return function ScriptCache(scripts: any) {
const Cache: any = {}
Cache._onLoad = function (key: any) {
return (cb: any) => {
let stored = scriptMap.get(key);
if (stored) {
stored.promise.then(() => {
stored.error ? cb(stored.error) : cb(null, stored)
})
} else {
// TODO:
}
}
}
Cache._scriptTag = (key: any, src: any) => {
if (!scriptMap.has(key)) {
let tag : any = document.createElement('script');
let promise = new Promise((resolve: any, reject: any) => {
let resolved = false,
errored = false,
body = document.getElementsByTagName('body')[0];
tag.type = 'text/javascript';
tag.async = false; // Load in order
const cbName = `loaderCB${counter++}${Date.now()}`;
let cb: any;
let handleResult = (state: any) => {
return (evt: any) => {
let stored = scriptMap.get(key);
if (state === 'loaded') {
stored.resolved = true;
resolve(src);
// stored.handlers.forEach(h => h.call(null, stored))
// stored.handlers = []
} else if (state === 'error') {
stored.errored = true;
// stored.handlers.forEach(h => h.call(null, stored))
// stored.handlers = [];
reject(evt)
}
cleanup();
}
}
const cleanup = () => {
if (global[cbName] && typeof global[cbName] === 'function') {
global[cbName] = null;
}
}
tag.onload = handleResult('loaded');
tag.onerror = handleResult('error')
tag.onreadystatechange = () => {
handleResult(tag.readyState)
}
// Pick off callback, if there is one
if (src.match(/callback=CALLBACK_NAME/)) {
src = src.replace(/(callback=)[^\&]+/, `$1${cbName}`)
cb = (window as any)[cbName] = tag.onload;
} else {
tag.addEventListener('load', tag.onload)
}
tag.addEventListener('error', tag.onerror);
tag.src = src;
body.appendChild(tag);
return tag;
});
let initialState = {
loaded: false,
error: false,
promise: promise,
tag
}
scriptMap.set(key, initialState);
}
return scriptMap.get(key);
}
Object.keys(scripts).forEach(function (key) {
const script = scripts[key];
Cache[key] = {
tag: Cache._scriptTag(key, script),
onLoad: Cache._onLoad(key)
}
})
return Cache;
}
})(window)
export default ScriptCache;
Even though Container looks like this nothing will get printed:
export class Container extends React.Component<any, any> {
render()
{
const style = {
width: '100px',
height: '100px'
}
return (
<div style={style}>
<Map google={this.props.google} />
</div>
)
}
}
If I do however set width and height on map ref directly it will print correctly. You can not add width and height in percent.
export class Map extends React.Component<any, any> {
...
render() {
const style = {
width: '100vw',
height: '100vh'
}
return (
<div ref='map' style={style}>
Loading map...
</div>
)
}
}