8 – How can I add async and defer to every JavaScript file?

I would like to know how we can add async and defer in each js script

What I have tried so for.

  1. Using Advagg module in optimize js/css

  2. In theme.libraries.yml but it break the functionality

    Added parameter

    //cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.3.4/assets/owl.carousel.min.css: { type: external, attributes: { async: true } }
    
    js/aos.js: { attributes: { async: true } }
    

Custom file which created issue:

/**
* @file
* Global utilities.
*
*/
(function ($, Drupal) {

'use strict';

 Drupal.behaviors.mytheme = {
   attach: function (context, settings) {
   $('button.navbar-toggler').click(function() {
    if (!$('#superfish-main-accordion').hasClass('sf-expanded')) {
      $('#superfish-main-toggle').click().hide();
    }
  });

  $(window).scroll(function() {
if($('.view-community .view-content .views-row .views-field .field-content .aos-animate').length){
$('.commu_inner p').each(function() {
var $this = $(this),
    countTo = $this.attr('data-count');

$({ countNum: $this.text()}).animate({
  countNum: countTo
},

{

  duration: 1500,
  easing:'swing',
  step: function() {
    $this.text(Math.floor(this.countNum));
  },
  complete: function() {
    $this.text(this.countNum);
  }
});
}); 
}
  });
   if($(".reset-blog-serach").length < 1){
   //$("#edit-actions--3").remove();
  
   $(".node--type-blog-page .views-exposed-form .form-submit").parent().after("<button type='button' class='reset-blog-serach btn btn-primary'>Reset</button>");
}
if($(".reset-public-serach").length < 1){
   $(".node--type-public-landing-page input:text(name='combine')").after("<button type='button' class='reset-public-serach btn btn-primary'>Reset</button>");
}
$(".node--type-decode .speaker_bios .container .views-element-container .view-id-decode .row .col-lg-3 .views-field-nothing .field-content .onpoup").each(function() {  
            //alert('hello');
         $(this).on("click", function(){
        var showpopup = $(this).attr('class').split(' ')(1);
        //alert('hello');
        //alert(showpopup);
        $(".node--type-decode .container .views-element-container  .row .col-lg-3 .views-field-nothing .field-content .masks").each(function(){
        if($(this).hasClass(showpopup)){
            //alert('yes');
            $('.overlay').fadeIn();
            $(this).fadeIn();
            $('body').css('overflow-y', 'hidden');
          }
        });
         });

});

$(".close").click(function(){
    $(".masks").fadeOut('active');
    $('.overlay').fadeOut();
    $('body').css('overflow-y', 'scroll');
});

// Function for close the Modal
function closeModal(){
    $(".node--type-decode .container .views-element-container .view-id-decode .row .col-md-3 .views-field-nothing .field-content .col-md-3.masks").removeClass("active");
}

// Call the closeModal function on the clicks/keyboard

$(".close, .masks").on("click", function(){
    closeModal();
});



$(document).keyup(function(e) {
    if (e.keyCode == 27) {
        closeModal();
    }
}); 
        
// $(window).load(function() {
        
    // AOS.init({
        // duration: 1200,
    // });

// }); 
$('.blog_scription_button').removeClass('btn-primary');
$('#video_archive .container .views-element-container .view-id-decode .view-content .table-responsive .table tbody tr .views-field-field-title').each(function(){
var value = $(this).text();
//alert(value);
//alert(value.length);
if(value.length == '10'){ //true
   //alert('hello');
    $(this).next('td').attr("colspan", 2);
    $(this).next('td').addClass('tdcolspan2')
    $(this).remove();
    //$(this).next('td').attr("colspan", 2);
    //$(this).next('td').attr("colspan", 2); 
  
}
else{
    
}
});


 /*  window.addEventListener('load', async () => {
 $('#vid').play();
 });*/
    
$(document).ready(function() {
    
        $('.webinar_slider').owlCarousel({
            loop:true,
            margin:10,
            dots:false,
            autoplayTimeout: 7000,
            nav:true,
            mouseDrag:true,
            autoplay:true,
            addClassActive:true,
            responsive:{
                0:{
                    items:1
                },
                600:{
                    items:1
                }, 
                1000:{
                    items:1
                }
            }



        });
         if($(".vertical-nav").length > 0){
   $(".vertical-nav").slick({
    dots: false,
    infinite: true,
    speed: 500,
    vertical: true,
    slidesToShow: 1,
    slidesToScroll: 1,
    asNavFor: '.vertical-center',
  });
 }
 if($(".vertical-center").length > 0){
   $(".vertical-center").slick({
    dots: false,
    infinite: true,
    speed: 500,
    fade: false,
    vertical: true,
    slidesToShow: 4,
    slidesToScroll: 0,
    arrows: false,
    draggable: true,
    centerMode: true,
    focusOnSelect: true,
    asNavFor: '.vertical-nav',
  });
 }
        


/* End */
$('.navigation_about li a').each(function(e) {
$(this).on("click", function() {
var getid = $(this).attr('href');
//alert(getid);
$('html, body').animate({ scrollTop: $(getid).offset().top - 90 }, 1000);
});
});


$('.reset-blog-serach').on('click', function(e){
    e.preventDefault();

       
       $('body').append('<div class="ajax-progress ajax-progress-fullscreen">&nbsp;</div>');
       var base_url = window.location.origin;
       var blog_list_url = base_url + "/check-exists-entry"
       var elementSettings = {};
       elementSettings.url = blog_list_url;
       elementSettings.progress = {
        type: 'throbber'
        };
        var result =  Drupal.ajax(elementSettings).execute();
          result.done(function(msg){
          $(".ajax-progress.ajax-progress-fullscreen").remove();
        })
       
   
     
});

  /**************************** Start : Public Reset Button ******************/

 $('.reset-public-serach').on('click', function(e){
    e.preventDefault();

       
       $('body').append('<div class="ajax-progress ajax-progress-fullscreen">&nbsp;</div>');
       var base_url = window.location.origin;
       var publication_list_url = base_url + "/public-reset-button"
       var elementSettings = {};
       elementSettings.url = publication_list_url;
       elementSettings.progress = {
        type: 'throbber'
        };
        var result =  Drupal.ajax(elementSettings).execute();
          result.done(function(msg){
          $(".ajax-progress.ajax-progress-fullscreen").remove();
        })
       
    //}
     
});


  /*************************** End : Publication Reset Button ******************/
  /**************************** Start : blog relevence ******************/
  $(".form-item-blog-search-by").on('change', function(e){
    e.preventDefault();
       var text = $('option:selected', this).val();
       //console.log(text); 
       $('body').append('<div class="ajax-progress ajax-progress-fullscreen">&nbsp;</div>');
       if(text == 2){
        var base_url = window.location.origin;
       var publication_list_url = base_url + "/blogrelevence"
       var elementSettings = {};
       elementSettings.url = publication_list_url;
       elementSettings.progress = {
        type: 'throbber'
        };
       var result =  Drupal.ajax(elementSettings).execute();
      result.done(function(msg){
        $(".form-item-blog-search-by option(value=1)").removeAttr('selected');
        $(".form-item-blog-search-by option(value=2)").prop('selected', true);
        jQuery(".ajax-progress.ajax-progress-fullscreen").remove();
       })
       }else{
        console.log('jgfj')
          $('body').append('<div class="ajax-progress ajax-progress-fullscreen">&nbsp;</div>');
          var base_url = window.location.origin;
         var blog_list_url = base_url + "/check-exists-entry"
         var elementSettings = {};
         elementSettings.url = blog_list_url;
         elementSettings.progress = {
          type: 'throbber'
          };
          var result =  Drupal.ajax(elementSettings).execute();
          result.done(function(msg){
          $(".ajax-progress.ajax-progress-fullscreen").remove();
        })
       }
     });
 /*************************** End : blog relevence ******************/

 /**************************** Start : Publication Dropdown ******************/
 $(".public-dropdown input(type='checkbox')").on('click', function(e){
    e.preventDefault();
   $('body').append('<div class="ajax-progress ajax-progress-fullscreen">&nbsp;</div>');
    var termname = $(this).next('label').text();
    console.log("val" + $(this).attr('name') + $(this).val());
    var val = $(this).val();        
    var filtername = $(this).attr('data-drupal-selector');
      if(filtername == 'edit-created-2'){
        termname = filtername;
      }
    
  var elementSettings = {};
  elementSettings.url = "/publicdropdown";
  elementSettings.progress = {
    type: 'throbber'
  };
  elementSettings.submit = {
      termid: termname,
      
  };      
  var publication_dropdown = Drupal.ajax(elementSettings).execute();
  publication_dropdown.done(function(msg){
          $(".ajax-progress.ajax-progress-fullscreen").remove();
        })
     });
  /*************************** End : Publication Dropdown ******************/

 });
 }
}
jQuery( document ).ready(function() {

 $(document).on("click",".team_leader_button, .team_director_button, .team_advisor_button", function(e){
   e.preventDefault();
   $('#TeamModal .team_image').html("");
   $('#TeamModal .team_name').html("");
   $('#TeamModal .team_position').html("");
   $('#TeamModal .team_desc').html("");    
   var img = $(this).closest(".team").find(".team_leader_content_img").html();
   var name = $(this).closest(".team").find(".team_leader_name").text();
   var position = $(this).closest(".team").find(".team_leader_position").text();
   var desc = $(this).closest(".team").find(".team_leader_desc").text();
   console.log(name);
   $('#TeamModal .team_image').html(img);
   $('#TeamModal .team_name').html(name);
   $('#TeamModal .team_position').html(position);
   $('#TeamModal .team_desc').html(desc);
   $("#TeamModal").modal();
});

});
})(jQuery, Drupal);

thread safety – Concurrent, Async Generic Queue C#

I have written this implmentation to assist with a print queue in C#. I have borrowed a substantial amount of code from this answer on SO and modified slightly to include a custom async action.

Be glad of thoughts or how to improve it

Example Usage

    private static ConcurrentAsyncQueue<PrintJob> _printQueue;
    private static ConcurrentAsyncQueue<PrintJob> PrintQueue
    {
        get
        {
            if(_printQueue is null)
            {
                _printQueue = new ConcurrentAsyncQueue<PrintJob>(printJob =>
                {
                    try
                    {
                        // do stuff with printJob Synchronously
                    }
                    catch (Exception ex)
                    {
                        ex.Log();
                    }
                });

                // **** OR ASYNC VERSION ****

                _printQueue = new ConcurrentAsyncQueue<PrintJob>(async printJob =>
                {
                    try
                    {
                        // do stuff with printJob Asyncronously
                    }
                    catch (Exception ex)
                    {
                        await ex.LogAsync();
                    }
                });

            }

            return _printQueue;
        }
    }



public class ConcurrentAsyncQueue<T> : IDisposable where T : class
{
    private readonly ConcurrentQueue<T> _processingQueue = new ConcurrentQueue<T>();
    private Thread _worker;
    private volatile bool _workerTerminateSignal = false;
    private readonly EventWaitHandle _waitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);

    public bool HasQueuedItem
    {
        get
        {
            lock (_processingQueue)
            {
                return _processingQueue.Any();
            }
        }
    }

    public T NextQueuedItem
    {
        get
        {
            if (!HasQueuedItem)
                return null;

            lock (_processingQueue)
            {
                if (!_processingQueue.TryDequeue(out var result))
                    return null;
                
                return result;
            }
        }
    }

    public void Enqueue(T item)
    {
        lock (_processingQueue)
        {
            _processingQueue.Enqueue(item);
        }
        _waitHandle.Set();
    }

    private readonly Func<T, Task> _processingActionAsync;

    public ConcurrentAsyncQueue(Action<T> processingAction) : this(action =>
    {
        processingAction(action);
        return Task.CompletedTask;
    })
    {

    }

    public ConcurrentAsyncQueue(Func<T, Task> processingActionAsync)
    {
        _processingActionAsync = processingActionAsync ?? throw new Exception("Action cannot be null!");
        _worker = new Thread(async () => await ProcessQueueAsync());
        _worker.Start();
    }

    private async Task ProcessQueueAsync()
    {
        while (!_workerTerminateSignal)
        {
            if (!HasQueuedItem)
            {
                Debug.WriteLine("No items, waiting.");
                _waitHandle.WaitOne();
                Debug.WriteLine("Waking up...");
            }
            var item = NextQueuedItem;
            if (item == null) continue;
            Debug.WriteLine(string.Format("Worker processing item"));
            await _processingActionAsync(item);
        }
    }

    public void Dispose()
    {
        if (_worker != null)
        {
            _workerTerminateSignal = true;
            _waitHandle.Set();
            if (!_worker.Join(TimeSpan.FromMinutes(1)))
            {
                Debug.WriteLine("Worker busy, aborting the thread.");
                _worker.Abort();
            }
            _worker = null;
        }
    }
}

**UPDATE 12/02/2021 **

I did a bit more research and decided to go with BufferBlock<T> from System.Threading.Tasks.Dataflow

Here is my simple wrapper – makes it a lot easier!

public class ConcurrentAsyncQueue<T> where T : class
{
    private readonly BufferBlock<T> _jobs = new BufferBlock<T>();
    public ConcurrentAsyncQueue(Action<T> action) : this(a =>
    {
        action(a);
        return Task.CompletedTask;
    }) { }

    private readonly Func<T, Task> _asyncAction;
    public ConcurrentAsyncQueue(Func<T, Task> asyncAction)
    {
        _asyncAction = asyncAction;
        var thread = new Thread(new ThreadStart(async () => await OnStartAsync()))
        {
            IsBackground = true
        };
        thread.Start();
    }

    public void Enqueue(T obj)
    {
        _jobs.Post(obj);
    }
    private async Task OnStartAsync()
    {
        while (true)
        {
            var queueResult = await _jobs.ReceiveAsync();
            await _asyncAction(queueResult);
        }
    }
}

discord bot python – async def pega apénas uma frase e o ID

eu queria saber como faço para mandar varias frases de uma vez, exemplo

?say Hello World (IDChannel)

oque acontece é que eu só posso mandar uma frase e depois o ID, eu queria adicionar quantas frases eu quiser e depois o ID

@client.command(
    pass_context=True)
async def say(ctx, mensagem : str, id : int):
    amount = 1
    channel = client.get_channel(id)
    await ctx.channel.purge(limit=amount)
    await channel.send(mensagem)

unity – Using async, await and Task.Delay() in a recursive method

I’m developing a roguelike game in Unity using the RogueSharp library. Currently I’m stuck on realizing the turn-based combat aspect of the game (inspired by this tutorial), which is made up of a scheduling system that keeps track of the actors (i.e., the player and monsters) and handles the order of actions they can perform.

private async void ActivateMonsters()
{
    var scheduleable = SchedulingSystem.NextActor;
    UnityEngine.Debug.Log($"Turn of {scheduleable}!");

    if (scheduleable is Player)
    {
        IsPlayerTurn = true;
        SchedulingSystem.Add(Player);
    }
    else
    {
        if (IsPlayerDead)
        {
            return;
        }
        if (scheduleable is Monster monster)
        {
            UnityEngine.Debug.Log("Start monster turn!");

            if (await monster.PerformAction(InputCommand.None))
            {
                UnityEngine.Debug.Log("Monster action done!");

                SchedulingSystem.Add(monster);
            }
        }

        ActivateMonsters();
    }
}

At the bottom of the method, there is a recursive call to ActivateMonsters() for ensuring that all monsters perform their actions until the player has a turn again.

Since my roguelike game is graphical and not text-based, the monsters have animations for attacking, walking, etc., that should be displayed on every turn. Therefore, the game should wait for each monster to perform its attack including playing the corresponding animation, before moving on to the next monster’s turn. To achieve that, I’ve made the ActivateMonsters() method async and wait for monster.PerformAction() to end (the InputCommand.None input enum can be ignored).

ActivateMonsters() is initially called in this event handler method invoked in Unity’s Update() method:

private void UpdateGame(object sender, EventArgs e)
{
    if (IsPlayerTurn)
    {
        HandleKeyboardInput();
    }
    else
    {
        UnityEngine.Debug.Log("First ActivateMonsters() call!");
        ActivateMonsters();
        RenderRequired = true;
    }
}

monster.PerformAction() first calculates the next position of the monster via pathfinding in an async method, which then calls the following method:

public static async Task<bool> Move(Actor actor, Loc location)
{
    EnsurePropertiesAreSet();
    var previousActorPosition = actor.Loc;

    // Return true if moving the actor to the given location was successful.
    if (DungeonMap.SetActorPosition(actor, location))
    {
        if (actor is Monster monster)
        {
            var moveDirection = location - previousActorPosition;

            await Task.Delay(3000);

            // Move the monster on the map.
            Game.SetMonsterMoveDirection(monster, moveDirection);
        }

        return true;
    }

    return false;
}

For testing purposes, I put in the await Task.Delay(3000) before the monster moves to the next tile.

With this I get:

First ActivateMonsters() call!
Turn of BrainHero.RogueLike.Actors.Goblin!
Start monster turn!
First ActivateMonsters() call!
Turn of BrainHero.RogueLike.Actors.Player!
(3 second pause)
Monster action done!
Turn of BrainHero.RogueLike.Actors.Player!

However, what I want to get is:

First ActivateMonsters() call!
Turn of BrainHero.RogueLike.Actors.Goblin!
Start monster turn!
(3 second pause)
Monster action done!
Turn of BrainHero.RogueLike.Actors.Player!

The await Task.Delay(3000) part does work, but it seems like that the if (await monster.PerformAction(InputCommand.None)) is skipped and the code execution returns to UpdateGame() for some reason. How can I modify the code so that I can achieve the latter execution sequence?

Thanks in advance for any help in solving this issue!

c# – Cannot perform binding on null reference when on async operastion

I’ve got a series of functions and methods to authenticate to a web service and control various hardware parameters.

Of which, almost all of them are async – aside from the ones where I’m specifically steering some parameters.

Where I’ve come unstuck over the last few days (and finally can see now) is that I have set a new value, sent it to the server, and am waiting for a boolean to come back to tell me if it’s successful.

What’s worse – is that if I’m explicitly awaiting everything I still get the same error.

I’m got it called from a timer, so I can consolidate all updates into a single JSON object, and correctly serialise it for the endpoint.

private async void SendTimer_Elapsed(object sender, ElapsedEventArgs e)
{
    var timer = sender as Timer;
    timer.Stop();
    if (Device.JsonToSend != null && Device.JsonToSend.Count > 0)  //ensure I've got a valid dict of endpoints and JSON updates
    {
        foreach (var json in Device.JsonToSend)
        {
            //Key is path, Value is payload.
            
            if(await Device.putValue(json))  //true if update was successful
            {
                await Device.readConfigJsonFromServer(this);
                await Device.readStatusJsonFromServer(this);
            }
            logger.Debug($"JSON to Send:n{json}");
        }

    }
    Device.JsonToSend = new Dictionary<string, JObject>();
    timer.Start();

}

Timer above, putValue below.

public async Task<bool> putValue(KeyValuePair<string, JObject> json)
{
    // Initialization.  
    var handler = new HttpClientHandler()
    {
        ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
    };
    // Prepare JSON - serialise CONFIG token

    string s = JsonConvert.SerializeObject(json.Value.SelectToken("config"));
    dynamic tempJson = json.Value;
    tempJson.Value.SelectToken("config").Replace(s);


    using (var client = new HttpClient(handler))
    {
        // Access test instance
        client.BaseAddress = SNPDeviceUri;
        string putPath = "/api/elements/" + SNPDeviceUri.Host + json.Key; // construct my putValue query
        putPath += "/?partial=true";

        int retryCount = 3;
        do
        {
            --retryCount;
            // Setting content type.  
            HttpResponseMessage putResponse = null;
            try
            {
                //stuff
            }
            ...
                else if(putResponse.IsSuccessStatusCode)
                {
                    //DeviceModel.CommsStatus = DeviceState.Connected;
                    logger.Trace($"PUT Succeeded to {SNPDeviceUri.Host}{putPath}nt{putResponse.StatusCode}:{putResponse.ReasonPhrase}nnt{json.Value.ToString()}n");
                    return true;
                }

                Thread.Sleep(1000);
            } while (retryCount > 0);
            
         
        }
        return false;
    }

Even if I declare a boolean explicitly for await Device.putValue – I still get the same error – Cannot perform binding on null reference.

I think I’ve spent way too much time looking at this, and have evidently done something wrong.

Any thoughts?
Many thanks

java – Convert a callback based flow to async iterable

Let’s say I have some event-based flow, for example a “read only” websocket. Normally you set the “onmessage” callback to deal with incoming messages.
Is there some way to access the messages in an async iterator? My goal is to have

async () => {
    for await (const message of SOMETHING(new WebSocket('...'))) {
        // message is the content of the callback
    }
};

I have toyed with promises and queues, but I couldn’t find a way to convert a callback (called multiple times) to an (async-) iterator

Async function en Javascript NodeJS

Estoy tratando de actualizar la contraseña de un usuario ya registrado y para ello primero encripto la nueva contraseña y después se la cambio, he tratado de varias formas y solo logro que se bloquee porque no cumple la promesa o devuelva una password null

Esta es la función que llamo con un await:

function encriptarNueva(newPassword) {
    return new Promise(resolve => {
        bcrypt.hash(newPassword, null, null, (err, hash) => {
            resolve = hash;
        });
    });
}

Y esta la función para cambiar la contraseña:

async function changePassword(req, res) {
    var params = req.body;
    var email = params.email;
    var password = params.password;
    var newPassword = params.newPassword;
    var nuevaPass;
    let userId = req.params.id;
    
    // Evitar que un usuario actualice a otro usuario:
    if (userId != req.user.sub) {
        return res.status(500).send({
            message: 'No tienes permiso para actualizar la contraseña de este usuario'
        });
    }
    
    nuevaPass = await encriptarNueva(newPassword);
    
    User.findOne({email: email}, (err, user) => {
        if (err)
            return res.status(500).send({
                message: 'Error en la petición'
            });
        if (user) {
            bcrypt.compare(password, user.password, (err, check) => {
                if (check) {
                    params.password = nuevaPass;
                    User.findByIdAndUpdate(userId, params, {new : true}, (err, userUpdated) => {
                        if (err)
                            return res.status(500).send({
                                message: 'Error en la petición de actualizar la contraseña'
                            });
                        if (!userUpdated)
                            return res.status(404).send({
                                message: 'No se ha podido actualizar la contraseña'
                            });
                        return res.status(200).send({user: userUpdated});
                        });
                } else {
                    return res.status(404).send({
                        message: 'La contraseña actual es incorrecta.',
                        user: user.password,
                        newPassword: newPassword,
                    });
                }
            });
        } else {
            return res.status(404).send({
                message: 'El correo asociado es inexistente'
            });
        }
    });
}

Cualquier sugerencia es bienvenida, gracias de antemano.

SQL Server 2019 AlwaysOn DBCC CHECKDB on async replica setup as not readable

I’ve seen answers in this exchange that say DBCC CHECKDB should be able to run on AOAG synchronous replicas that are not open for read. That MS fixed that because it was a bug in 2016. I’m using SS 2019 CU8 Developer in QA/Test env and it will not allow integrity checks on the synchronous replica. Note I didn’t create the secondary failover/replica as readable as I don’t want to license the secondary.
Note I do run integrity checks on the primary – I would also like to run them on the secondary. After all – they do switch roles every now and then…for server and SS maintenance/patching.

SharePoint Framework PnP – How do I ensure my async method is completed in init() and super.onInit() promises

I have the below code.. How do I ensure my loadLookupLists() is completed before the onInit()/super.onInit() method returns

    @override  public onInit(): Promise<void> {
    Log.info(LOG_SOURCE, 'Initialized PanelCommandSetCommandSet');
 
    /*// Setup the PnP JS with SPFx context
    sp.setup({
      spfxContext: this.context
    });
 
    this.panelDomElement = document.body.appendChild(document.createElement("div"));
 
    return Promise.resolve();*/
    return super.onInit().then(_ => {
      // other init code may be present
      sp.setup({
        spfxContext: this.context
      });
      this.panelDomElement = document.body.appendChild(document.createElement("div"));
      // check for await here..
      this.loadLookupLists();
    });
  }
 
  private async loadLookupLists(): Promise<void> {       
    const allItems: any() = await sp.web.lists.getByTitle("Critical Applications").items.select("ID", "Application Name").getAll();
    //many more calls here.. would like to use pnp batch
    console.log(allItems);
  }

javascript – Abstracting async hooks out from components

A component makes displays a list of values and I’m trying to hide everything about how those values are gotten, essentially divorcing concerns about state away from the component itself so that in the future any changes to which API data is fetched from, what store is used, etc won’t effect the component.

First, is this a good idea at all or should mount/unmount actions be handled directly in the component?

Second, if it is a good approach, how is my implementation? I’m feeling comfortable in JS but am not an expert

ContentList.js:

import React from 'react'

import './ContentList.css'

import { ContentCardContainer } from '../ContentCard/ContentCard'
import { getPostSummaries } from '../../Utils/ContentAPI'

import { Grid } from '@material-ui/core'

export function ContentListContainer(props) {
  const posts = getPostSummaries()

  return <ContentList posts={posts} />
}

function ContentList(props) {
  return  (
    <div className='content-list'>
      <Grid container >
        {props.posts.map((post, id) => 
          <Grid item xs={12} sm={12} lg={6}>
            <ContentCardContainer 
              key={id} 
              id={id} 
              title={post.title} 
              summary={post.summary} 
              link={'post/' + post.title} 
            />
          </Grid>
        )}
      </Grid>
    </div>
  )
}

ContentApi.js:

import { useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { setContent, setSummaries } from '../Store/Actions'
import axios from 'axios'

const apiRoot = '/api'

export const getPostByID = (id) => {    
    const dispatch = useDispatch()
   
    useEffect(() => {
      axios.get(apiRoot+'/post', {params: {id}})
        .then(resp => dispatch(setContent(resp.data.content)))
        .catch(() => dispatch(setContent('')))
      
        return () => dispatch(setContent(''))
    }, ())

    return useSelector(state => state.content)
}

export const getPostSummaries = () => {
  const dispatch = useDispatch()

  useEffect(() => {
    axios.get(apiRoot+'/post-summaries')
      .then(resp => dispatch(setSummaries(resp.data.posts)))
      .catch(() => dispatch(setSummaries(())) )
  }, ())

  return useSelector(state => state.summaries)
}