const accounts = await web3.eth.getAccounts();
App = {
load: async () => {
await App.loadWeb3(
await App.loadAccount()
)
},
loadWeb3: async () => {
if (typeof web3 !== 'undefined') {
App.web3Provider = web3.currentProvider
web3 = new Web3(web3.currentProvider)
} else {
window.alert("Please connect to Metamask.")
}
// Modern dapp browsers...
if (window.ethereum) {
window.web3 = new Web3(ethereum)
try {
// Request account access if needed
await ethereum.enable()
// Acccounts now exposed
web3.eth.sendTransaction({/* ... */})
} catch (error) {
// User denied account access...
}
}
// Legacy dapp browsers...
else if (window.web3) {
App.web3Provider = web3.currentProvider
window.web3 = new Web3(web3.currentProvider)
// Acccounts always exposed
web3.eth.sendTransaction({/* ... */})
}
// Non-dapp browsers...
else {
console.log('Non-Ethereum browser detected. You should consider trying MetaMask!')
}
},
loadAccount: async () => {
App.account = web3.eth.accounts[0]
console.log(App.account)
}
}
$(() => {
$(window).load(() => {
App.load()
})
})
The error is in LINE 1 where I get the accounts from Ganache but await is valid only for async.
What changes should I make in this code to remove the error? Please help me.
If I remove this line the error says that it cannot access accounts and after this await does not work.
Is there any way to make this piece of code in the form of an ASYNC function?
await calls can only be made in functions marked as async. As you have used await in line 1 it is not wrapped in an async function. You can wrap your code in a async function and then call that function. e.g something like:
const main = async () => { // <- the async wrapper function
const accounts = await web3.eth.getAccounts();
// .... rest of your code
$(() => {
$(window).load(() => {
App.load()
})
})
}
main()
Or if you want to be more advanced and not save the function at all
(async ()=>{
const accounts = await web3.eth.getAccounts();
// .... rest of your code
})() // <- call the function right after declaring it
I'm new to next js. And I have one user.js file inside of my pages directory in next.js. This is the source code:
// pages/user.js
function user(props) {
const [userList, setUserList] = useState([])
const [roleList, setRoleList] = useState([])
async function initialFetch() {
const userList = await fetch('GET', GET_ALL_USERS)
setUserList(userList)
const roleList = await fetch('GET', GET_ALL_ROLES)
setRoleList(roleList)
console.log('userList in async')
console.log(userList)
}
if (!props.userList.status) {
initialFetch()
} else {
setUserList(props.userList)
setRoleList(props.roleList)
}
console.log('userList outside')
console.log(userList)
return (
<>
<TableUserManagement users={userList} roles={roleList}/>
</>
)
};
user.getInitialProps = async (ctx) => {
const userList = await fetch('GET', GET_ALL_USERS)
const roleList = await fetch('GET', GET_ALL_ROLES)
return {userList, roleList}
}
The problem is that above async initialFetch() function is always called uninfinitively :
So what am I doing wrong here? Thank you
Note: I have tried to use useEffect() but the looping still happening. This the code :
useEffect(
() => {
if (!props.userList.status) {
initialFetch()
} else {
setUserList(props.userList)
setRoleList(props.roleList)
}
console.log('user list diliuar')
console.log(userList)
}
)
This issue is not related to Next.js but React itself. This is the code that cause unfinite calls:
if (!props.userList.status) {
initialFetch()
} else {
setUserList(props.userList)
setRoleList(props.roleList)
}
Since after setting any state, your component re-renders and that part of code keeps running again, and the fetch cause setting state again,... that loops forever.
You should move you data-fetching logic in side componentDidMount or useEffect. Remember to provide the dependency array of useEffect. In this case, you may only need to fetch data only once so you should provide the empty dependency array.
useEffect(() => {
async function initialFetch() {
const userList = await fetch('GET', GET_ALL_USERS)
setUserList(userList)
const roleList = await fetch('GET', GET_ALL_ROLES)
setRoleList(roleList)
}
if (!props.userList.status) {
initialFetch()
} else {
setUserList(props.userList)
setRoleList(props.roleList)
}
}, []);
P/s: you should name you React component in PascalCase (ex: User)
I’ve created some React files where one initializes a Redux store. However, I really need to load some data from a json file before store is initialized.
I’ve tried to import a script loading the json structure then assigning it to the createStore initialState value. But createStore runs before the data is loaded and assigned.
Is there any simple way to say “dont do anything before my axios call is done”???
Action types
actiontypes.js
export const LOAD_DATA_REQUEST='LOAD_DATA_REQUEST';
export const LOAD_DATA_SUCCESS='LOAD_DATA_SUCCESS';
export const LOAD_DATA_ERROR='LOAD_DATA_ERROR';
Actions
actions.js
import * as Actions from './actiontypes';
function load() {
return { type: Actions.LOAD_DATA_REQUEST };
}
function success(res) {
return { type: Actions.LOAD_DATA_SUCCESS, payload: res };
}
function error(ex) {
return { type: Actions.LOAD_DATA_ERROR, payload: ex };
}
export function loadData(url) {
return (dispatch) => {
dispatch(load());
axios.get(url).then((res) => {
dispatch(success(res));
}).catch((ex) => {
dispatch(error(ex));
});
};
}
use this in reducers that requires
import * as Actions from './actiontypes';
const newState = Object.assign({}, state);
switch (action.type) {
case Actions.LOAD_DATA_REQUEST:
{
//maybe you load
newState.loading = true;
return newState;
}
case Actions.LOAD_DATA_SUCCESS:
{
const res = action.payload;
//do what you need for this reducer
return newState;
}
case Actions.LOAD_DATA_ERROR:{
/// maybe you will want to show some error message in some reducer?
return newState;
}
}
You just need the first screen of your application on componentWillMount() call the loadData() action
I hope this can help you
I have a sample app called GraphQL Bookstore that creates books, publishers and authors and shows relationships between them. I am using subscriptions to show updates in real time.
For some reason my BOOK_ADDED subscription is bypassing the graphql wrapper completely. It is calling the wrapped class with the books prop set to undefined. Relevant parts of the code are shown below (you can see the full code here).
class BooksContainerBase extends React.Component {
componentWillMount() {
const { subscribeToMore } = this.props;
subscribeToMore({
document: BOOK_ADDED,
updateQuery: (prev, { subscriptionData }) => {
if (!subscriptionData.data) {
return prev;
}
const newBook = subscriptionData.data.bookAdded;
// Don't double add the book
if (!prev.books.find(book => book.id === newBook.id)) {
return Object.assign({}, prev, {
books: [...prev.books, newBook]
});
} else {
return prev;
}
}
});
}
render() {
const { books } = this.props;
return <BooksView books={books} />;
}
}
...
export const BooksContainer = graphql(BOOKS_QUERY, {
props: ({ data: { loading, error, subscribeToMore, books } }) => ({
loading,
error,
subscribeToMore,
books
})
})(LoadingStateViewer(BooksContainerBase));
Basically when a subscription notification is received by the client, the updateQuery() function is called - as expected. However, as soon as that function exits, the render() method of the wrapped class is called directly with books set to undefined. I expected that the graphql wrapper would be called, setting the props correctly before calling the render() method. What am I missing?
Thanks in advance!
For all I know, I have to write request in action create. How to use a promise in action for submitting a request? I am getting data in action. Then new state is created in reducer. Bind action and reducer in connect. But I don't know how to use promise for request.
Action
import $ from 'jquery';
export const GET_BOOK = 'GET_BOOK';
export default function getBook() {
return {
type: GET_BOOK,
data: $.ajax({
method: "GET",
url: "/api/data",
dataType: "json"
}).success(function(data){
return data;
})
};
}
Reducer
import {GET_BOOK} from '../actions/books';
const booksReducer = (state = initialState, action) => {
switch (action.type) {
case GET_BOOK:
return state;
default:
return state;
}
};
export default booksReducer;
Container
How display data in container?
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import getBook from '../actions/books';
import Radium from 'radium';
import {Link} from 'react-router';
function mapStateToProps(state) {
return {
books: state.data.books,
};
}
function mapDispatchToProps(dispatch) {
return {
getBooks: () => dispatch(getBook()),
};
}
#Radium
#connect(mapStateToProps, mapDispatchToProps)
class booksPage extends Component {
static propTypes = {
getBooks: PropTypes.func.isRequired,
books: PropTypes.array.isRequired,
};
render() {
const {books} = this.props;
return (
<div>
<Link to={`/authors`}><MUIButton style="flat">All Authors</MUIButton></Link>
<ul>
{books.map((book, index) =>
<li key={index}>
<Link to={`/book/${book.name}`}><MUIButton style="flat"><div class="mui--text-black mui--text-display4">
"{book.name}"</div></MUIButton></Link>
<Link to={`/author/${book.author}`}><MUIButton style="flat"><div class="mui--text-black mui--text-display4">
{book.author}</div></MUIButton></Link>
</li>
)}
</ul>
</div>
);
}
}
export default booksPage;
Since you are already using redux you can apply redux-thunk middleware which allows you to define async actions.
Installation & usage: Redux-thunk
export function fetchBook(id) {
return dispatch => {
dispatch(setLoadingBookState()); // Show a loading spinner
fetch(`/book/${id}`, (response) => {
dispatch(doneFetchingBook()); // Hide loading spinner
if(response.status == 200){
dispatch(setBook(response.json)); // Use a normal function to set the received state
}else {
dispatch(someError)
}
})
}
}
function setBook(data) {
return { type: 'SET_BOOK', data: data };
}
You should use Async Actions described in Redux Documentation
Here an example of reducer for async action.
const booksReducer = (state = {}, action) => {
switch (action.type) {
case 'RESOLVED_GET_BOOK':
return action.data;
default:
return state;
}
};
export default booksReducer;
and then you create your Async Action.
export const getBook() {
return fetch('/api/data')
.then(response => response.json())
.then(json => dispatch(resolvedGetBook(json)))
}
export const resolvedGetBook(data) {
return {
type: 'RESOLVED_GET_BOOK',
data: data
}
}
Several Notes:
We could return Promise (instead of Object) in action by using redux-thunk middleware.
Don't use jQuery ajax library. Use other library specifically for doing that (e.g. fetch()). I use axios http client.
Remember, in redux you only use pure function in reducer. Don't make ajax call inside reducer.
Read the complete guide from redux docs.
You should be able to use dispatch inside the callback (if you pass it as an argument):
export default function getBook(dispatch) {
$.ajax({
method: "GET",
url: "/api/data",
dataType: "json"
}).success(function(data){
return dispatch({type:'GET_BOOK', data: data});
});
}
Then, pass dispatch to the action:
function mapDispatchToProps(dispatch) {
return {
getBooks: () => getBook(dispatch),
};
}
Now, you should have access to the action.data property in the reducer:
const booksReducer = (state = initialState, action) => {
switch (action.type) {
case GET_BOOK:
//action.data <--- here
return state;
default:
return state;
}
};
You might want to separate concerns, to keep action creators "pure".
Solution; write some middleware. Take this for example (using superagent).
import Request from 'superagent';
const successHandler = (store,action,data) => {
const options = action.agent;
const dispatchObject = {};
dispatchObject.type = action.type + '_SUCCESS';
dispatchObject[options.resourceName || 'data'] = data;
store.dispatch(dispatchObject);
};
const errorHandler = (store,action,err) => {
store.dispatch({
type: action.type + '_ERROR',
error: err
});
};
const request = (store,action) => {
const options = action.agent;
const { user } = store.getState().auth;
let method = Request[options.method];
method = method.call(undefined, options.url)
if (user && user.get('token')) {
// This example uses jwt token
method = method.set('Authorization', 'Bearer ' + user.get('token'));
}
method.send(options.params)
.end( (err,response) => {
if (err) {
return errorHandler(store,action,err);
}
successHandler(store,action,response.body);
});
};
export const reduxAgentMiddleware = store => next => action => {
const { agent } = action;
if (agent) {
request(store, action);
}
return next(action);
};
Put all this in a module.
Now, you might have an action creator called 'auth':
export const auth = (username,password) => {
return {
type: 'AUTHENTICATE',
agent: {
url: '/auth',
method: 'post',
resourceName: 'user',
params: {
username,
password
}
}
};
};
The property 'agent' will be picked up by the middleware, which sends the constructed request over the network, then dispatches the incoming result to your store.
Your reducer handles all this, after you define the hooks:
import { Record } from 'immutable';
const initialState = Record({
user: null,
error: null
})();
export default function auth(state = initialState, action) {
switch (action.type) {
case 'AUTHENTICATE':
return state;
case 'AUTHENTICATE_SUCCESS':
return state.merge({ user: action.user, error: null });
case 'AUTHENTICATE_ERROR':
return state.merge({ user: null, error: action.error });
default:
return state;
}
};
Now inject all this into your view logic. I'm using react as an example.
import React from 'react';
import ReactDOM from 'react-dom';
/* Redux + React utils */
import { createStore, applyMiddleware, bindActionCreators } from 'redux';
import { Provider, connect } from 'react-redux';
// thunk is needed for returning functions instead
// of plain objects in your actions.
import thunkMiddleware from 'redux-thunk';
// the logger middleware is useful for inspecting data flow
import createLogger from 'redux-logger';
// Here, your new vital middleware is imported
import { myNetMiddleware } from '<your written middleware>';
/* vanilla index component */
import _Index from './components';
/* Redux reducers */
import reducers from './reducers';
/* Redux actions*/
import actionCreators from './actions/auth';
/* create store */
const store = createStore(
reducers,
applyMiddleware(
thunkMiddleware,
myNetMiddleware
)
);
/* Taint that component with store and actions */
/* If all goes well props should have 'auth', after we are done */
const Index = connect( (state) => {
const { auth } = state;
return {
auth
};
}, (dispatch) => {
return bindActionCreators(actionCreators, dispatch);
})(_Index);
const provider = (
<Provider store={store}>
<Index />
</Provider>
);
const entryElement = document.getElementById('app');
ReactDOM.render(provider, entryElement);
All of this implies you already set up a pipeline using webpack,rollup or something, to transpile from es2015 and react, to vanilla js.
Consider using the new thunk API
export const load = createAsyncThunk(
'example/api',
async (arg, thunkApi) => {
const response = await fetch('http://example.api.com/api')
if (response.status === 200) {
const json = await response.json()
return json
},
)
Also, in the new redux template application, actions are part of the reducer/slice, and you can use extraReducers to response to events related to the async action status. It is much simpler using redux this way.
See documentation of async thunk here: https://redux.js.org/usage/writing-logic-thunks