I want to load data form an Auth0 useUser hook, which can only be called from a page functional component.
that's fine but when i return jsx, the hook call is async. and the data is not available to be used when the jsx component is rendered (ie return <Home user={data} /> the data is from a hook call and Home renders before data is available) and data is only available after Home is rendered.
Please is there a better approach to achieve this?
I want data from hooks to be available during rendering of a page.
import getUser from './api/hooks/user';
import Homepage from './home';
function Controller ({userAuth}) {
//2. it is only acceptable to be called here
//let user = getUser();
return <Homepage user={userAuth} />
}
Controller.getInitialProps = async (context) =\> {
//1. this is wrong cause nextjs disallows it
let user = getUser();
//3. but the getInitialProps, getStaticProps and getServerSideProps passes data to the page component as //props and renders the page only when data is available.
return{userAuth: user}
}
export default Controller;
//4. Also I Used
import { getSession } from '#auth0/nextjs-auth0';
Controller.getInitialProps = async (context) =\> {
//5. the user object from getUser Hook is present there.
let user = getSession();
//6. but sometimes user.user returns undefined
return{userAuth: user.user}
}
Related
As you can see from the picture below, I'm rendering the popover using a react-relay QueryRenderer since the request is slow, and I do not want the rest of the page to wait for events to be fetched.
My problem is that in the navigation I have a button to show/hide the popover. That button should only be rendered when events has loaded, and the button also needs to show a count of how many events there is.
So my question is how to pass events data up from QueryRenderer (popover) to a parent component (toggle button)?
My first idea was to reuse my QueryRenderer for events and pass in dataFrom={'STORE_ONLY'}, to avoid a new HTTP request and use the cache instead, but unfortunately 'STORE_ONLY' is not an option... YET...
From looking at https://github.com/relay-tools/relay-hooks/issues/5 it seems like store-only will be supported by useQuery in the future, so is that the recommended solution to go about it, or how is the recommended way? Surely facebook, and many other applications, must have had this need frequently?
You can achieve redux-like relay store with custom handlers and local schema.
I'll be guessing what your queries, components and fields might be named like so don't forget to change it to correct values
Somewhere in project's src folder create a file ClientState.client.graphql to extend your root query type with new field for client state:
// ClientState.client.graphql
type ClientState {
showToggleButton: Boolean!
eventsCount: Int
}
extend type Query {
clientState: ClientState!
}
this will allow you to wrap Toggle button with fragment like this:
fragment ToggleButton_query on Query {
clientState {
showToggleButton
eventsCount
}
}
and spread this fragment in parent query (probably AppQuery)
Then in your second query, where you'll be fetching events, add #__clientField directive, to define custom handle for that field:
query EventModal {
events #__clientField(handle: "eventsData") {
totalCount
}
}
Create EventsDataHandler for handle eventsData:
// EventsDataHandler.js
// update method will be called every time when field with `#__clientField(handle: "eventsData")` is fetched
const EventsDataHandler = {
update (store, payload) {
const record = store.get(payload.dataID)
if (!record) {
return
}
// get "events" from record
const events = record.getLinkedRecord(payload.fieldKey)
// get events count and set client state values
const eventsCount = events.getValue('totalCount')
const clientState = store.getRoot().getLinkedRecord('clientState')
clientState.setValue(eventsCount, 'eventsCount')
clientState.setValue(true, 'showToggleButton')
// link "events" to record, so the "events" field in EventModal is not undefined
record.setLinkedRecord(events, payload.handleKey)
}
}
export default EventsDataHandler
Last thing to do is to assign custom (and default) handlers to environment and create init store values:
// environment.js
import { commitLocalUpdate, ConnectionHandler, Environment, RecordSource, Store, ViewerHandler } from 'relay-runtime'
import EventsDataHandler from './EventsDataHandler'
// ...
const handlerProvider = handle => {
switch (handle) {
case 'connection':
return ConnectionHandler
case 'viewer':
return ViewerHandler
case 'eventsData':
return EventsDataHandler
default:
throw new Error(`Handler for ${handle} not found.`)
}
}
const environment = new Environment({
network,
store,
handlerProvider
})
// set init client state values
commitLocalUpdate(environment, store => {
const FIELD_KEY = 'clientState'
const TYPENAME = 'ClientState'
const dataID = `client:${FIELD_KEY}`
const record = store.create(dataID, TYPENAME)
record.setValue(false, 'showToggleButton')
// prevent relay from removing client state
environment.retain({
dataID,
variables: {},
node: { selections: [] }
})
store.getRoot().setLinkedRecord(record, FIELD_KEY)
})
In Vapor, specifically in the class for a custom Leaf tag, how can you retrieve values stored in a context?
I'm trying to implement a tag that takes a string and a path, and renders a link unless the path is the current page, so, for example, #navElement("About Us", "/about") will produce a link to the site's about page on every page except the about page itself. On that page, it should display the text without a link on it.
I don't want to have to pass the current path to the tag every time I use it, so I've stored the request's path in the context, roughly like this (checks omitted):
drop.get(":page"){ request in
return try drop.view.make(thePage, ["path": request.uri.path])
}
I can use #(path) in a template and see the path I expect.
My custom tag is derived from Tag, and its run method receives the context as an argument, and I can see the stored value in there in the debugger – but how do I get at it? The get method in the Context class, which seems to do this, is internal, so I can't use it. There is a comment that says subscripts are to be done, and I assume that this will ultimately be the way to extract values from the context, but in the meantime, is there any way to retrieve them?
Just make the current path one of the arguments to your tag.
Droplet route:
drop.get(":page") { request in
return try drop.view.make(thePage, ["currentPath": request.uri.path])
}
In template:
#navElement("About Us", "/about", currentPath)
Tag:
class NavElement: Tag {
let name = "navElement"
public func run(stem: Stem, context: LeafContext, tagTemplate: TagTemplate, arguments: [Argument]) throws -> Node? {
guard
let linkText = arguments[0].value?.string,
let linkPath = arguments[1].value?.string,
let currentPath = arguments[2].value?.string
else { return nil }
if linkPath == currentPath {
return Node("We are at \(currentPath)")
} else {
return Node("Link \(linkText) to \(linkPath)")
}
}
}
Edit:
I have spoken to the developers of Vapor, and they do not intend to open up access to Context's contents publicly. However, since the queue: List<Node>() is public, you can just copy the get() function into your own extension and then you'll be able to do as you wanted.
I am building a Single Page Application using ASP.NET and sammy.js, where all views except for the Home/Index view are rendered as partial views so that sammy can swap out the content of the main body with the partial view that is returned.
I am using the example given here, and everything loads fine as expected.
Similar to the above example, in my Home/Index page I have reference to a script called routing.js, which wraps the sammy function call in order to parse the MVC route:
var Routing = function (appRoot, contentSelector, defaultRoute) {
function getUrlFromHash(hash) {
var url = hash.replace('#/', '');
if (url === appRoot)
url = defaultRoute;
return url;
}
return {
init: function () {
Sammy(contentSelector, function () {
this.get(/\#\/(.*)/, function (context) {
var url = getUrlFromHash(context.path);
context.load(url).swap();
});
}).run('#/');
}
};
}
I need to call a callback function after the content swap has fully completed in order to implement further jQuery functionality on the newly rendered content. My dilemma is that no matter what option I try from the sammy.js docs, nothing seems to run the callback after the content has been swapped.
I have tried all of the following (all "valid" ways of passing a callback according to the sammy.js docs):
content.load(url).swap(pageLoadScripts(url));
content.load(url).swap().onComplete(pageLoadScripts(url));
content.load(url).swap().then(pageLoadScripts(url));
content.load(url).swap().next(pageLoadScripts(url));
content.load(url,pageLoadScripts(url)).swap();
and even
content.load(url).swap();
pageLoadScripts(url);
In every case the pageLoadScripts function fires off prior to the content being swapped. Any ideas or suggestions on what to do differently?
This is a bit of a hack, but it works.
Inside the Sammy initialization function, I added the following override to the swap function just before the override to the get function:
this.swap = function (content, callback) {
var context = this;
context.$element().html(content);
pageLoadScripts(hashedUrl);
};
FWIW, I still have not been able to get callback to be anything other than 'undefined', even in this override function.
Managed to get this working:
// override for callback after page load
this.swap = function(content, callback) {
this.$element().html(content);
if (callback) {
callback();
}
};
// users
this.get('/#/users', function(context) {
context.load('/users').swap(function() { replaceBindings(viewModel.users); });
});
I managed to get the callback param to NOT be 'undefined' by wrapping it in another function.
I have a POST method in mvc which saves a customer and it redirects to Index View by default.
Now I want to include a notification if the customer is created. I have tried the basic SignalR implementation but I am not sure how I can invoke SignalR from an ActionMethod so that it will be reflected in client.
My confusion is redirecting to Index and invoking the client using SignalR can be written in the same Acton method, or is it required to use other method from the client after the customer is saved.
Here is the code:
[HttpPost]
public ActionResult SaveCustomer(Customer customer)
{
//Save Customer
db.Customer.Add(customer);
db.SaveChanges();
// Notify Client something similar this
//$.connection.hub.start().done(function () {
//chat.server.send(customer.Name);
//});
ChatHub chat = new ChatHub();
chat.Clients.Send(customer.Name)
RedirectToAction("Index");
}
In my Layout page I have messagenotificationPartialView
$(function () {
// Reference the auto-generated proxy for the hub.
var chat = $.connection.chatHub;
// Create a function that the hub can call back to display messages.
chat.client.send = function (customerName) {
//add to a span
};
I am not sure if I am in the right direction. Is this the right way or are there any other options to implement this?
I have a page with links. These links all end in the same way. For example www.site.com/fruit/apples, www.site.com/fruit/bananas, www.site.com/fruit/oranges, etc. I want all these links to call the same Dart app and have the app do some processing and then redirect you wherever you need to go (the bananas page vs. the oranges page). This way, I avoid having an actual HTML file for every single fruit. I can instead have a single landing template that gets populated with variable fruit data.
The part I'm hung up on is passing the url into the Dart app so it can do the handling. I understand main() cannot receive arguments, so what's another way?
You can use the route package to handle the URL's for you.
For example:
import 'package:route/client.dart';
final fruitUrl = new UrlPattern(r'/fruit/(\w+)');
main() {
var router = new Router()
..addHandler(fruitUrl, showFruit)
..listen();
}
void showFruit(String path) {
var fruit = fruitUrl.parse(req.path)[0];
// Display the page according to the fruit type
}
If you don't need to handle actual routes, and you just want to handle any query parameters passed of the form ?fruit=apple you don't have to use the routes package and can instead manually parse the URL:
Map params = {};
// If arguments were provided, decode them into params map
if(window.location.search.length > 1) {
// Break all arguments into form: fruit=apple
List<String> queryPairs = window.location.search.substring(1).split('&');
for(String queryPair in queryPairs) {
// Add arguments into params map: key=fruit, value=apple
List<String> queryPairList = queryPair.split('=');
params[queryPairList[0]] = queryPairList[1];
}
}
// Handle the proper action based on the fruit
switch(params['fruit']) {
case 'apple':
// ...
break;
// ...
case 'orange':
// ...
break;
}