How to use Hook inside Nextjs middleware? - graphql

I have a middleware like this
import type { NextFetchEvent, NextRequest } from "next/server";
import { NextResponse } from "next/server";
import { useCheckIsAdminQuery } from "../../generated/graphql";
export function middleware(req: NextRequest, ev: NextFetchEvent) {
const {data,loading,error} = useCheckIsAdminQuery()
if(data?.checkIsAdmin){
return NextResponse.next()
}else{
const url = req.nextUrl.clone()
url.pathname = '/404'
return NextResponse.redirect(url)
}
}
useCheckIsQuery() just a hook was generated by codegen package, but I cannot call this hook inside middleware.
The error display
Invalid hook call. Hooks can only be called inside of the body of a function component.
How can i fix it?

According to React docs: "Don't call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function, before any early returns.".
Nextjs' middleware is not a React function, so you cannot use hooks inside it. The solution to your problem is to create a hook with your custom logic, and manually calling it in the page you'd like to take effect.

Related

how to implement HOC as hooks with states

I want to make a HOC that returns a functional component instead of returning a class-based component (Because I want to update ContextProvider). Is it really possible to do it?
The problem with the functional component is that I need to define several states inside the functional component and based on that return the wrapped component but as we know the states could not be defined inside the return statement and if I move the useStates above the return basically this will not be HOC and any components that do use this way of implementation get "JSX element type '...' does not have any construct or call signatures" error.
Class-based:
const HOC(WrappedComponent)=>return class extends React.Component {
// do some state stuff also use componentDidMount
return(){
return <wrappedComponent {...someNewProps from state}
}
}
If I use hooks like this to define HOC
const HOC(WrappedComponent)=>{
// if I use useState stuff and useEffect here ( which should be outside return) then I get "JSX element type '...' does not have any construct or call signatures" error for any components that use such type of HOC
return <wrappedComponent {...someNewProps from state}
}

class property doesn't update with redux

Context
I've built an app that renders mails from your outlook accounts into a web page in react.
I'm trying to set a "viewed" boolean as a class property fed by redux store and change it from within the component (that change must impact in redux to manage that change on the overall app )
Problem
As you might see on below's code, i initiate the instance variable in the constructor with the given information from redux reducer,
I've tested with a bunch of console logs if the action creator successfully updates that information on the store and it actually does.
My problem is that my instance variable (this.viewed) isn't updating with redux's reducer information (that actually does update)
import React from "react"
import {connect} from "react-redux"
import { bindActionCreators} from "redux"
import * as QueueActions from "../redux/actions/actionCreators/queueActions"
class Mail extends React.Component {
constructor (props){
super(props)
this.id = props.id
this.viewed = props.mails.find(mail => mail.id = this.id).viewed
}
}
componentDidMount = () => {
this.props.queueActions.setMailToViewed(this.id);
}
function mapStateToprops () {
return {
mails : store.queueReducer.mails,
}
}
function mapDispatchToProps() {
return {
queueActions : bindActionCreators( QueueActions, dispatch ),
}
}
export default connect ( mapStateToprops, mapDispatchToProps ) (Mail)
Question
what am i doing wrong here?
why does the viewed property on redux updates but my instance variable that feeds from that very same information doesn't?
shouldn't this.viewed update whenever the props that provided the information update?
Can't i update this information from props without using a state?
I think the issue is because the assignment to this.viewed happens in the constructor, which is only called once. When the redux store updates, the component will get new props but the constructor will not be called again, so the value will not be updated. Hopefully these links will help explain the issue:
ReactJS: Why is passing the component initial state a prop an anti-pattern?
https://medium.com/#justintulk/react-anti-patterns-props-in-initial-state-28687846cc2e
I'd also recommend reading up on functional components v class components and why functional components are used alot now instead of class ones. A starting point:
https://medium.com/#Zwenza/functional-vs-class-components-in-react-231e3fbd7108
If you used a functional component, you could use the useSelector hook to access the store and update your components.
Hope this this useful, I'm quite new to react so apologies if you're looking for something more, but I hope this helps.

How to call one method globally across components in react native

I have a method called submit() in one component like eg. leave.js.
I have declared all the router navigation in router.js component.
I have placed a button in leave component header which is in router.js to apply the leave.
Now, i want to call this method from the click event of the button which is placed in the header.
How can i call this method globally from another component.
Kindly help me with a solution.
Thanks in Advance,
Regards,
Janani
I have tried the below link but its not working for me.
https://github.com/kriasoft/react-starter-kit/issues/909
When you create and use a function file, it's very useful.
Example
let name = "name";
function setName(data) {
name = data
}
function getName() {
return name
}
export { setName, getName }
import {setName, getName } from "./functionpath"
componentWilmount(){
setName("John");
}
componentDidmount(){
const name = getName();
alert(name);
}

Load objects with parameters from Laravel IoC container

I need a way to load objects via IoC provider depending on a request parameter. Right now I'm loading my objects directly with App::make(xy, [$urlParamter] which I want to refactor, so that I can use dependency injection the way it is supposed to. To describe my current architecture I need to show you quiet some information and in the end you find my concrete questions I have about it.
I'm building up a general CMS framework which provides an import architecture that is extendable with a custom import implementation.
Now I'm struggling with properly loading the concrete classes via IoC container, because they always depend on the selected import.
To dig into my problem, here is my entry point in routes.php
Route::get('/import', ['as' => 'overview', 'uses' => '\CMSFramework\Http\Controllers\Import\ImportController#index']);
This generates a view where the user selects a concrete import to be triggered. After selecting a concrete import, the user should get individual views to prepare the appropriate import (i.e. Upload a CSV file, select an area to import, etc.)
In my concept an import implementation consist of:
A controller class, to implement specific (peraration-) tasks like uploading a CSV file. It inherits from a base controller of the cms framework
An import "business" or "service" class, that implements how the data is getting imported (and may further delegate to queued jobs etc.)
The CMS framework part consists of:
A base controller class for all common/shared import tasks like (start the prepared import, clean all working data, etc.)
A base service class ImportBase where all implementations inherit from. It provides an interface to receive a progress for any import and implements shared operations like cleaning up working data, etc.)
An ImportStatus class which is part of the ImportBase-Class via $ImportBase->status() to handle all runtime status informations (like "is the job still running, what is the progress). This class also provides a containter for a so called "payload" that allows any conrete import implementation to push and fetch custom status informations (ie. any sub-process has been finished)
So back to my IoC architecture. After the user selected a concrete import, the following route delegates the action to the custom import implementation's controller. If it's a framework supported standard-action like via URL /import/<importkey>/clean, the inherited BaseController of the cms framework takes over and handles the request
Route::get('/import/{key}/{method}', ['uses' => function($key, $method) {
return App::make('\\MadeleinePim\\Http\\Controllers\\Import\\'.ucfirst(camel_case($key)).'Controller')->$method($key);
}]);
I know that this direct binding via a naming convention can be improved (maybe via a custom configuration file), but for now this works for me.
Now I need to show an example of how I tried to implement a concrete import target in my controller via /import/<importkey>/seedCsvDataToDatabase:
public function seedCsvDataToDatabase($key)
{
// The IoC binding is shown in next code snippet. I did not found a good way to use method injection because
// of the route-specific parameters that control the responsible import implementation
$import = \App::make(Import::class, [$key]);
// Now trigger the import service operation of that concrete import implementation (probably bad design here)
$import->seed();
// Now, that this preparation task is done, I use the ImportStatus object which is part of the Import to store
// status informations. With this I can then decided in which step the user is (Think of it like a wizard to
// prepare any import)
$import->status()
->set(ConcreteImport::STATUS_SEEDED, true)
->set(ConcreteImport::STATUS_SEEDED_DURATION_SECONDS, (microtime(true) - $time_start) / 60);
// Back to controller method that determines in which status the import is to delegate/redirect to different
// views.
return redirect('/import/<importkey>');
}
My IoC binding for the Import class:
$this->app->singleton(Import::class, function ($app, array $parameters) {
$importKey = head($parameters);
// There is a config file that provides the class names of the concrete import implementations
$importClassName = config()->get('import.' . $importKey);
if (!$importClassName) {
throw new ImportNotFoundException($importKey, "Import with key '{$importKey}' is not setup properly'");
}
$importReflectionClass = new \ReflectionClass($importClassName);
return $importReflectionClass->newInstance($importKey);
});
And finally, the lazy loading of the import status, which is encapsulated in the ImportStatus object looks like this
public function status()
{
if (!$this->status) {
$this->status = \App::make(ImportStatus::class, [$this->key()]);
}
return $this->status;
}
I hope that demonstrates the way I try to resolve my import objects from the IoC container.
My learning so far is, that this is not the right way to inject my objects.
Is the assumption right, that I should not pass the $importKey at runtime to the App::make() and rather should try to make this independ?
My failed attempt on this was to make the IoC binding smarter and let it access the Request to properly inject my concrete import object with the required $importKey, like (pseudo code!):
$this->app->bind(ImportStatus::class, function(Container $app) {
// Did not find a good way to access the {key}-part of my route /import/{key}/{method}
$key = $app->make(Request::class)->get('key'); // Does not work like this
return new \Scoop\Import\ImportStatus($key);
});
Does this approach can work like this?
Can I somehow pass through the $importKey from my route to the ServiceProvider (or better pull it from there?)
Is there a better solution to initialize my concrete import implementations?
----------
UPDATE 1
For my lattest idea to access the Route in my IoC Binding, I got this way working:
$this->app->singleton(Import::class, function (Container $app) {
$importKey = \Route::current()->getParameter('key');
$importClassName = config()->get('import.' . $importKey);
$importReflectionClass = new \ReflectionClass($importClassName);
return $importReflectionClass->newInstance($importKey);
});
Nevertheless the idea of #Sandyandi N. dela Cruz to use a router binding prevents the direct coupling between the Binding and the Request which still doesn't feel right. Using router-binding to couple a request parameter to an implementation, sounds more appropriate.
I think you've dwelt to much on the IoC container there. Why not implement the Factory pattern and do a route binding instead of creating multiple controllers to handle different Imports? Crude example as follows:
Create a route binder - edit your app/Provider/RouteServiceProvider.php's boot() method
public function boot(Router $router)
{
parent::boot($router);
// Add the statement below.
$router->bind('import', 'App\RouteBindings#import');
}
Create the App\RouteBindings class as app/RouteBindings.php
Create an import() method with the following:
public function import($importKey, $route)
{
switch ($importKey) {
case 'import_abc':
return new ImportAbc;
break; // break; for good measure. ;)
case 'import_xyz':
return new ImportXyz;
break;
// and so on... you can add a `default` handler to throw an ImportNotFoundExeption.
}
}
Create a route for resolving an Import class.
Route::get('import/{import}/{method}', 'ImportController#handleImport');
Here, {import} will return the proper Import concrete class based on your URL.
In your ImportController's handleImport() you can do the following:
public function handleImport(Import $import, $method)
{
// $import is already a concrete class resolved in the route binding.
$import->$method();
}
So when you hit: http://example.com/import/import_abc/seed, the route binding will return a concrete class of ImportAbc and store it in $import on your handleImport() method, then your handleImport() method will execute: $import->seed();. Tip: you should probably move other controller logic such as $import->status()->set() into the Import class. Keep your controllers thin.
Just make sure your Import classes have the same signature.
It's kinda like Laravel's Route Model Binding except you create the logic for the bindings.
Again, this is just a crude example but I hope it helps.

Zend framework 2 : Add different authentication adapter for two different modules

I have two different modules. Now I need to add different authentication mechanism for both modules.
So I added event code first module's Module.php's onBootstrap method
$listener = $serviceManager->get('First\Service\AuthListener');
$listener->setAdapter($serviceManager->get('First\Service\BasicAuthAdapter'));
$eventManager->attach(MvcEvent::EVENT_ROUTE, $listener, 0);
and in second module's Module.php's onBootstrap method
$listener = $serviceManager->get('Second\Service\AuthListener');
$listener->setAdapter($serviceManager->get('Second\Service\AdvAuthAdapter'));
$eventManager->attach(MvcEvent::EVENT_ROUTE, $listener, 0);
Now if I disable one of modules, functionality working fine and request properly authenticated. While enabling both module do some kind of overlapping So even required module properly authenticated, But other module event code also got executed and system give not authenticated error.
I am thinking this due to event handler code in both module.php is executed without take care of requested module url.
I can verify with requested route pattern before authentication, But that is look like a hack instead of good solution.
If better solution exists for handling this issue ?
UPDATE :
My AuthListener Code :
namespace First\Service;
use Zend\Authentication\Adapter\AdapterInterface;
use Zend\Mvc\MvcEvent;
class AuthListener
{
protected $adapter;
public function setAdapter(AdapterInterface $adapter)
{
$this->adapter = $adapter;
}
public function __invoke(MvcEvent $event)
{
$result = $this->adapter->authenticate();
if (!$result->isValid()) {
$response = $event->getResponse();
// Set some response content
$response->setStatusCode(401);
$routeMatch = $event->getRouteMatch();
$routeMatch->setParam('controller', 'First\Controller\Error');
$routeMatch->setParam('action', 'Auth');
}
}
}
There is a good way to make module specific bootstrap - to use SharedManager:
$e->getApplication()->getEventManager()->getSharedManager()
->attach(__NAMESPACE__, 'dispatch', function(MvcEvent $e) {
// This code will be executed for all controllers in current __NAMESPACE__
}, 100);
Here is a good article to understand difference between EventManager and SharedEventManager
There is no additional info about listeners in the question, but I try to guess:
If you use as listener some callable class - it's ok, just replace function() { } by your $listener.
If you use as listener some class, that implements
ListenerAggregateInterface, you should convert listeners to
SharedListenerAggregateInterface and use method attachAggregate
instead of attach
I hope it helps!

Categories

Resources