I'm not asking for help I just think you have something wrong with the custom json-server implementation.
I'm using: "admin-on-rest": "^1.1.0"
This is my App.js:
...
import restClient from './restClient';
const App = () => (
<Admin
title="Glasses"
dashboard={Dashboard}
menu={Menu}
restClient={restClient}
authClient={authClient}
>
<Resource name="products" list={ProductList} />
</Admin>
);
The restClient.js file is a copy of https://github.com/marmelab/admin-on-rest/blob/master/src/rest/jsonServer.js I just changed the import paths like this:
import {
GET_LIST,
GET_ONE,
GET_MANY,
GET_MANY_REFERENCE,
CREATE,
UPDATE,
DELETE,
fetchUtils
} from 'admin-on-rest';
const { queryParameters, fetchJson } = fetchUtils;
...
The rest of the restClient.js is one by one as in the git "jsonServer.js".
When I click on "products" in my menu, I got a notification: "REST response must contain a data key" and in the console: "Warning: Missing translation for key: "REST response must contain a data key"
Now of course it looks like the server doesn't return "data" object in the response BUT the problem is that there is no even request! when I go to my "networks" tab (in chrome console filter to All networks) I don't see any request to the API so how it is possible to receive this kind of error?
I added "console.log" to the first row of this code in the bottom of my restClient.js file (jsonServer.js):
return (type, resource, params) => {
console.log('test'); // THIS WHAT I ADDED
// json-server doesn't handle WHERE IN requests, so we fallback to calling GET_ONE n times instead
if (type === GET_MANY) {
return Promise.all(params.ids.map(id => httpClient(`${apiUrl}/${resource}/${id}`)))
.then(responses => ({ data: responses.map(response => response.json) }));
}
const { url, options } = convertRESTRequestToHTTP(type, resource, params);
return httpClient(url, options)
.then(response => convertHTTPResponseToREST(response, type, resource, params));
};
but the "test" is not been printed to the console.
Any advice? Did I do something wrong?
Video which can help: https://nimbus.everhelper.me/client/notes/share/982310/35f8rwpu6qohrftn8h1h
The restClient.js file: http://pasted.co/2024e00f
Thank you in advanced.
Leo.
So my mistake was where I declared the restClient attribute inside the Admin component without URL.
This is the right way to do it:
const App = () => (
<Admin
title="Express Glasses"
dashboard={Dashboard}
menu={Menu}
restClient={restClient('http://UrlToApi.com/api')}
authClient={authClient}
>
<Resource name="products" list={ProductList} />
</Admin>
);
Related
In a regular React App I'd use Redux to manage the state, where I'd dispatch the initial data before matching any route in App, however, Redux is not advised in Remix, so I'm using useContext instead.
Is there a way to call loaders to fetch initial data (e.g. session, objects, etc.) before/without having to match any route and to then store that data in the context global store and then can be accessed by any component whithin the store? That way, the API will only be called during app initialization.
I'm at this moment calling the initial data in the loader of root.tsx, getting it with useLoaderData and then passing it as a prop to StoreProvider to dispatch it in the global state, however, I don't think this should be done like that way.
export let loader: LoaderFunction = async ({ request }) => {
let user = await getUser(request);
const products = await db.product.findMany();
return { user: user?.username, products };
};
function App() {
const data = useLoaderData<LoaderData>();
return (
<html lang="en">
...
<StoreProvider initData={data}>
<body>
...
<Outlet />
<ScrollRestoration />
<Scripts />
{process.env.NODE_ENV === "development" && <LiveReload />}
</body>
</StoreProvider>
</html>
);
}
export default App;
I think doing the data loading on the root route loader is the best way.
If you don't like that approach you could also fetch on entry.server and entry.client.
For example in entry.client you probably have something like this:
import { hydrate } from "react-dom";
import { RemixBrowser } from "remix";
hydrate(<RemixBrowser />, document);
So you can change it to do the fetch before calling hydrate.
import { hydrate } from "react-dom";
import { RemixBrowser } from "remix";
fetch(YOUR_API_ENDPOINT)
.then(response => response.json())
.then(data => {
hydrate(
<YourContextProvider value={data}>
<RemixBrowser />
</YourContextProvider>,
document
)
});
And in entry.server you can change the handleRequest function to something like this:
import { renderToString } from "react-dom/server";
import { RemixServer } from "remix";
import type { EntryContext } from "remix";
export default async function handleRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
remixContext: EntryContext
) {
let response = await fetch(YOUR_API_ENDPOINT)
let data = await response.json()
let markup = renderToString(
<YourContextProvider value={data}>
<RemixServer context={remixContext} url={request.url} />
</YourContextProvider>
);
responseHeaders.set("Content-Type", "text/html");
return new Response("<!DOCTYPE html>" + markup, {
status: responseStatusCode,
headers: responseHeaders
});
}
By doing it on entry.client and entry.server the fetch will only happen once and it will never be triggered again.
I still recommend you to do it inside the loader of the root so after an action it can be fetched again to keep the data updated.
For MS Botframework webchat, Is there a way to intercept user message before being rendered in webchat and change it?
This is easy to accomplish using the createStore() method.
In the web chat script located in your page, create the store using the above method. In it, match the action.type to 'WEB_CHAT/SEND_MESSAGE'. This will capture every message that is passed thru the web chat component before it is displayed.
Be aware, this altered text (or whatever value you are changing) is what is sent to the bot. action is the root object. action.payload, effectively, represents the activity. This is where you will find the text value, etc.
Within the if statement, perform whatever change you are looking to make, then return the action object.
Lastly, include the store object within the renderWebChat component. This should set you up.
In the example below, I am appending text to the text field altering it before it is rendered and displayed.
<script>
( async function () {
const res = await fetch( 'http://somesite/directline/token', { method: 'POST' } );
const { token } = await res.json();
// We are using a customized store to add hooks to connect event
const store = window.WebChat.createStore( {}, ( { dispatch } ) => next => action => {
if ( action.type === 'WEB_CHAT/SEND_MESSAGE' ) {
action.payload.text = action.payload.text + ' (Hello from behind the curtain)'
}
return next( action );
} );
window.WebChat.renderWebChat( {
directLine: window.WebChat.createDirectLine( { token } ),
userID: 'user123',
username: 'johndoe',
botAvatarInitials: 'BB',
userAvatarInitials: 'JD',
store
}, document.getElementById( 'webchat' ) );
document.querySelector( '#webchat > *' ).focus();
} )().catch( err => console.error( err ) );
</script>
Hope of help!
Please lemme know I'm stuck past 3 days.I'm newbie to Redux Saga I'm not understanding where I'm going wrong. As I've just started dont have much experience, not understood the sagas that well:( .But I wanna get onto it.
I need to show sales data in line graph following is my code
Sample Json Data
{"data":[{"year":"2014","Sales":4390,"Orders":3800},{"year":"2013","Sales":4490,"Orders":4300},{"year":"2015","Sales":2200,"Orders":3400},{"year":"2016","Sales":1280,"Orders":2398},{"year":"2017","Sales":5000,"Orders":4300},{"year":"2018","Sales":4780,"Orders":2908},{"year":"2019","Sales":5890,"Orders":4800}]}
Admin Folder
adminActions.js
import {GET_SALES_DATA} from '../constants/actionTypes';
export const getSales =()=>({
type:GET_SALES_DATA
});
adminReducer.js
import {GET_SALES_DATA,SALES_DATA_RECEIVED} from '../constants/actionTypes';
const adminReducer=(state={},action)=>{
switch(action.type){
case GET_SALES_DATA:
return {...state};
case SALES_DATA_RECEIVED:
return {...state,payload:action.salesDataPerYear} **//issue here payload data is not received state is not getting updated with the data**
default:
return state;
}
}
export default adminReducer;
adminSaga.js
import {put,takeLatest,all} from 'redux-saga/effects';
import {GET_SALES_DATA,SALES_DATA_RECEIVED} from '../constants/actionTypes';
function *getSales(){
console.log("5saga")
const salesDataPerYear=yield fetch("api call")
.then(response=>response.data);
yield put({type:SALES_DATA_RECEIVED,salesDataPerYear:salesDataPerYear})
}
function *actionWatcher(){
yield takeLatest(GET_SALES_DATA,getSales)
}
export default function *rootSaga(){
yield all([
actionWatcher(),
]);
}
dashboard.js
const mapStateToProps=(state)=>({
data:state.salesDataPerYear
})
const mapDispatchToProps = {
getSales:getSales
};
class DashboardMain extends React.Component {
componentDidMount(){
this.props.getSales()
}
render() {
const { classes,data } = this.props;
// const { data } =this.state;
**console.log(data)// data not rendering in console is the saga i've written is correct?**
return (
<div className={classes.root}>
<Grid container spacing={24}>
<Grid item xs={12}>
<SimpleLineChart data={data} />**//commented**
</Grid>
<Grid item xs={12}>
<SimpleTable />
</Grid>
</Grid>
</div>
);
}
}
DashboardMain.propTypes = {
classes: PropTypes.object.isRequired,
};
const Dashboard=connect(mapStateToProps,mapDispatchToProps)(DashboardMain);
simleLineChart.js (UPDATED)
<LineChart data={ this.props.data }> **// TypeError: Cannot read property 'props' of undefined**
Following is store folder
index.js
import createSagaMiddleware from 'redux-saga';
import {createStore,applyMiddleware} from 'redux';
import {logger} from 'redux-logger';
import rootSaga from '../adminDashboard/adminSaga';
import reducers from '../adminDashboard/adminReducers';
const sagaMiddleware=createSagaMiddleware();
const store=createStore(
reducers,
applyMiddleware(sagaMiddleware,logger),
);
sagaMiddleware.run(rootSaga);
export default store;
Data is not shown in line chart.I know there's some error in my saga but I dont know what is it :(.
Can anyone please lemme know where I'm going wrong. Anything which I've missed onto. Any help is appreciated.
Updates
Data not rendering in console is the saga I've written is corrector I've missed onto something?
undefined
redux-logger.js:389 action GET_SALES_DATA # 12:38:06.302
redux-logger.js:400 prev state {}
redux-logger.js:404 action {type: "GET_SALES_DATA"}type: "GET_SALES_DATA"__proto__: Object
redux-logger.js:413 next state {}
adminSaga.js:7 5saga
Response {type: "cors", url: "API call", redirected: false, status: 200, ok: true, …}body: (...)bodyUsed: falseheaders: Headers {}ok: trueredirected: falsestatus: 200statusText: "OK"type: "cors"url: "API call"__proto__: Response
redux-logger.js:389 action SALES_DATA_RECEIVED # 12:38:07.103
redux-logger.js:400 prev state {}__proto__: Object
redux-logger.js:404 action {type: "SALES_DATA_RECEIVED", payload: Response, ##redux-saga/SAGA_ACTION: true}payload: Responsebody: (...)bodyUsed: falseheaders: Headers {}ok: trueredirected: falsestatus: 200statusText: "OK"type: "cors"url: "API call"__proto__: Responsetype: "SALES_DATA_RECEIVED"##redux-saga/SAGA_ACTION: true__proto__: Object
redux-logger.js:413 next state {payload: undefined}payload: undefined__proto__: Object
I've commented the simpleLineChart there some issue with the saga and reducer I dont get the result.I updated with the log please check. the API call is retriving the data. But in reducer I'm doing wrong which I've no idea.Please lemme know
It worked like a charm horray:) after three days ufff .I learnt much worthy.the saying goes true to me "We learn more from our mistakes" Thanks to #RussCoder
adminSaga.js
try{
const salesDataPerYear=yield fetch("https://api.myjson.com/bins/78b9w")
.then(response=>response.json())
yield put({type:SALES_DATA_RECEIVED,payload:salesDataPerYear.data})
}
adminReducer.js
case SALES_DATA_RECEIVED:
return {...state,payload:action.payload };
dashbord.js
const mapStateToProps=state=>{
return{
dataSales:state //have changes data to dataname
}
};
I have a simple reactjs application which talks to a rest api and want it to serve the catch all route when the api returns a 404.
Since I'm using react-router-v4 I tried this.props.history.push("/404"); which does serve the correct component but also changes the URL in der browser.
This change of the URL is what I don't want
I Have the following code
App.js
class App extends Component {
render() {
return (
<Switch>
<Route exact path='/' component={Calendar}/>
<Route path='/day/:number' component={Day}/>
<Route component={Error404}/>
</Switch>
);
}
}
and in Day.js I do something like
componentDidMount() {
const dayNumber = parseInt(this.props.match.params.number);
jQuery.ajax('http://localhost:9018/api/day/' + dayNumber)
.done((result) => {
this.setState({
day: result
});
})
.fail(() => {
this.props.history.push("/404");
});
}
So when I surf to a URL like /no-match the app renders the Error404 component while the URL stays on /no-match <- correct
When I surf to /day/1 the app renders the Day component and backend returns 200. URL stays at /day/1 <- correct
When I surf to /day/35 the app renders the Day component and backend returns 404. Redirect renders Error404 component and URL changes to /404 <- This I don't want.
So I get that pushing to history is probably wrong.
But how can I implement a generalized solution that renders Error404 for every failed api call in the whole application?
You could set the state 'status' depending on the result:
componentDidMount() {
const dayNumber = parseInt(this.props.match.params.number);
jQuery.ajax('http://localhost:9018/api/day/' + dayNumber)
.done((result) => {
this.setState({
day: result, status: 'SUCCESS',
});
})
.fail(() => {
this.setState({ status: 'ERROR' })
});
}
And then, in your rendering, you can show the component you like:
render() {
return (
<div>
{this.state.status === 'SUCCESS' ? 'show day content' : 'show ErrorComponent'}
</div>
);
}
Depending on your whole app context createMemoryHistory from react-router might be worth a look as well, but from what I know it is only used for tests/native apps.
I am busy with a little proof of concept where basically the requirement is to have the home page be a login screen when a user has not logged in yet, after which a component with the relevant content is shown instead when the state changes upon successful authentication.
I have to state upfront that I am very new to react and redux and am busy working through a tutorial to get my skills up. However, this tutorial is a bit basic in the sense that it doesn't deal with connecting with a server to get stuff done on it.
My first problem was to get props to be available in the context of the last then of a fetch as I was getting an error that this.props.dispatch was undefined. I used the old javascript trick around that and if I put a console.log in the final then, I can see it is no longer undefined and actually a function as expected.
The problem for me now is that nothing happens when dispatch is called. However, if I manually refresh the page it will display the AuthenticatedPartialPage component as expected because the localstorage got populated.
My understanding is that on dispatch being called, the conditional statement will be reavaluated and AuthenticatedPartialPage should display.
It feels like something is missing, that the dispatch isn't communicating the change back to the parent component and thus nothing happens. Is this correct, and if so, how would I go about wiring up that piece of code?
The HomePage HOC:
import React from 'react';
import { createStore, combineReducers } from 'redux';
import { connect } from 'react-redux';
import AuthenticatedPartialPage from './partials/home-page/authenticated';
import AnonymousPartialPage from './partials/home-page/anonymous';
import { loggedIntoApi, logOutOfApi } from '../actions/authentication';
import authReducer from '../reducers/authentication'
// unconnected stateless react component
const HomePage = (props) => (
<div>
{ !props.auth
? <AnonymousPartialPage />
: <AuthenticatedPartialPage /> }
</div>
);
const mapStateToProps = (state) => {
const store = createStore(
combineReducers({
auth: authReducer
})
);
// When the user logs in, in the Anonymous component, the local storage is set with the response
// of the API when the log in attempt was successful.
const storageAuth = JSON.parse(localStorage.getItem('auth'));
if(storageAuth !== null) {
// Clear auth state in case local storage has been cleaned and thus the user should not be logged in.
store.dispatch(logOutOfApi());
// Make sure the auth info in local storage is contained in the state.auth object.
store.dispatch(loggedIntoApi(...storageAuth))
}
return {
auth: state.auth && state.auth.jwt && storageAuth === null
? state.auth
: storageAuth
};
}
export default connect(mapStateToProps)(HomePage);
with the Anonymous LOC being:
import React from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { loggedIntoApi } from '../../../actions/authentication';
export class AnonymousPartialPage extends React.Component {
constructor(props) {
super(props);
}
onSubmit = (e) => {
e.preventDefault();
const loginData = { ... };
// This is where I thought the problem initially occurred as I
// would get an error that `this.props` was undefined in the final
// then` of the `fetch`. After doing this, however, the error went
// away and I can see that `props.dispatch is no longer undefined
// when using it. Now though, nothing happens.
const props = this.props;
fetch('https://.../api/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(loginData)
})
.then(function(response) {
return response.json();
})
.then(function(data) {
if(data && data.jwt) {
props.dispatch(loggedIntoApi(data));
localStorage.setItem('auth', JSON.stringify(data));
}
// else show an error on screen
});
};
render() {
return (
<div>
... onSubmit gets called successfully somewhere in here ...
</div>
);
}
}
export default connect()(AnonymousPartialPage);
the action:
// LOGGED_INTO_API
export const loggedIntoApi = (auth_token) => ({
type: 'LOGGED_INTO_API',
auth: auth_token
});
// LOGGED_OUT_OF_API
export const logOutOfApi = (j) => ({
type: 'LOG_OUT_OF_API'
});
and finally the reducer:
const authDefaultState = { };
export default (state = authDefaultState, action) => {
switch (action.type) {
case 'LOGGED_INTO_API':
// SOLUTION : changed this line "return action.auth;" to this:
return { ...action.auth, time_stamp: new Date().getTime() }
case 'LOG_OUT_OF_API':
return { auth: authDefaultState };
default:
return state;
}
};
My suggestion would be to make sure that the state that you are changing inside Redux is changing according to javascript's equality operator!. There is a really good answer to another question posted that captures this idea here. Basically, you can't mutate an old object and send it back to Redux and hope it will re-render because the equality check with old object will return TRUE and thus Redux thinks that nothing changed! I had to solve this issue by creating an entirely new object with the updated values and sending it through dispatch().
Essentially:
x = {
foo:bar
}
x.foo = "baz"
dispatch(thereWasAChange(x)) // doesn't update because the x_old === x returns TRUE!
Instead I created a new object:
x = {
foo:"bar"
}
y = JSON.parse(JSON.stringify(x)) // creates an entirely new object
dispatch(thereWasAChange(y)) // now it should update x correctly and trigger a rerender
// BE CAREFUL OF THE FOLLOWING!
y = x
dispatch(thereWasAChange(y)) // This WON'T work!!, both y and x reference the SAME OBJECT! and therefore will not trigger a rerender
Hope this helps!