#StackBounty: #javascript #reactjs #typescript #react-native React Native check context value on click test case

Bounty: 200

I have following context

Home.tsx

export const ThemeContext = React.createContext(null)

const Home = () => {
  const { width } = Dimensions.get("window")
  const [theme, setTheme] = React.useState({
    active: 0,
    heightOfScrollView: 0,
    profileWidth: width * 0.2,
    scrolledByTouchingProfile: false
  })
  
  const horizontalScrollRef = React.useRef<ScrollView>()
  const verticalScrollRef = React.useRef<ScrollView>()

  return (
    <>
      <SafeAreaView style={styles.safeAreaContainer} />
      <Header title="Contacts" />
      <ThemeContext.Provider value={{ theme, setTheme }}>

In component A, I have a button which changes in the context

const onProfileTouched = (index: number) => {
  setTheme({ ...theme, active: index });
};

This leads to an image being active

const ImageCircle = ({ active, uri }: Props) => {
  return (
    <View
      style={
        active
          ? { ...styles.parentView, ...styles.active }
          : { ...styles.parentView }
      }>
      <Image source={uri} width={30} height={30} />
    </View>
  );
};

Now, I want to write a test case (I haven’t written a test case before) that confirms that the state has actually changed or perhaps an active border is added to the image

I added a testId to my button which I used to fire an event

it('changes active on profile clicked', () => {
  const { getByTestId } = render(<Home />);
  fireEvent.press(getByTestId('HScroll3.button'));
});

Now, I am unsure, how to grab the value of context or change in style so as I can confirm that indeed the component for which the button is pressed is active

I am using import {render, fireEvent} from '@testing-library/react-native' but open to change.


Get this bounty!!!

#StackBounty: #reactjs #spring #file-saver Cannot open downloaded zip file from rest endpoint using file-saver

Bounty: 50

Here is the backend code for the download endpoint:

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ZipOutputStream zipOut = new ZipOutputStream(byteArrayOutputStream);
for (Long id : ids) {
    // Get the "generated" file using the id    
    zipOut.putNextEntry(new ZipEntry(generated.getName() + ".doc"));
    InputStream inputStream = new ByteArrayInputStream(generated.getFile().getBytes(1, (int)generated.getFile().length()));
    IOUtils.copy(inputStream, zipOut);
    zipOut.closeEntry();
}
zipOut.close();

response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment; filename=File.zip");

byte[] zipBytes = byteArrayOutputStream.toByteArray();
OutputStream outputStream = response.getOutputStream();
outputStream.write(zipBytes);
outputStream.close();
response.flushBuffer();

And for the frontend, I am using axios and file-saver

import { saveAs } from "file-saver";

request.then((response: any) => {
  const blob = new Blob([response.data], { type: "application/zip" });
  saveAs(blob, "Report.zip");
});

I can download the zip file, but when I tried to open, I got the follwing error:

"An attempt was made to move the file pointer before the beginning of the file"


Get this bounty!!!

#StackBounty: #reactjs #typescript #spring-boot #material-ui #react-typescript Implement pagination for React Material table

Bounty: 200

I have this Spring Boot endpoint for listing items from database:

import React, { useEffect, useState } from "react";

import clsx from "clsx";
import {
  createStyles,
  lighten,
  makeStyles,
  Theme,
} from "@material-ui/core/styles";
import CircularProgress from "@material-ui/core/CircularProgress";
import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
import TableContainer from "@material-ui/core/TableContainer";
import TableHead from "@material-ui/core/TableHead";
import TablePagination from "@material-ui/core/TablePagination";
import TableRow from "@material-ui/core/TableRow";
import TableSortLabel from "@material-ui/core/TableSortLabel";
import Toolbar from "@material-ui/core/Toolbar";
import Typography from "@material-ui/core/Typography";
import Paper from "@material-ui/core/Paper";
import Checkbox from "@material-ui/core/Checkbox";
import IconButton from "@material-ui/core/IconButton";
import Tooltip from "@material-ui/core/Tooltip";
import DeleteIcon from "@material-ui/icons/Delete";
import FilterListIcon from "@material-ui/icons/FilterList";
import axios, { AxiosResponse } from "axios";
import { getTask } from "../../service/merchants";

const baseUrl = "http://185.185.126.15:8080/api";

interface OnboardingTaskDto {
  id?: number;
  name: string;
}

async function getTask(
  page: number,
  size: number
): Promise<AxiosResponse<OnboardingTaskDto[]>> {
  return await axios.get<OnboardingTaskDto[]>(
    `${baseUrl}/management/onboarding/task?page=${page}&size=${size}`
  );
}

interface Data {
  id: number;
  businessName: string;
  title: string;
  status: string;
}

function createData(
    id: number,
    businessName: string,
    title: string,
    status: string
): Data {
  return { id, businessName, title, status };
}

function descendingComparator<T>(a: T, b: T, orderBy: keyof T) {
  if (b[orderBy] < a[orderBy]) {
    return -1;
  }
  if (b[orderBy] > a[orderBy]) {
    return 1;
  }
  return 0;
}

type Order = "asc" | "desc";

function getComparator<Key extends keyof any>(
    order: Order,
    orderBy: Key
): (
    a: { [key in Key]: number | string },
    b: { [key in Key]: number | string }
) => number {
  return order === "desc"
      ? (a, b) => descendingComparator(a, b, orderBy)
      : (a, b) => -descendingComparator(a, b, orderBy);
}

function stableSort<T>(array: T[], comparator: (a: T, b: T) => number) {
  const stabilizedThis = array.map((el, index) => [el, index] as [T, number]);
  stabilizedThis.sort((a, b) => {
    const order = comparator(a[0], b[0]);
    if (order !== 0) return order;
    return a[1] - b[1];
  });
  return stabilizedThis.map((el) => el[0]);
}

interface HeadCell {
  disablePadding: boolean;
  id: keyof Data;
  label: string;
  numeric: boolean;
}

const headCells: HeadCell[] = [
  { id: "id", numeric: false, disablePadding: true, label: "id" },
  {
    id: "businessName",
    numeric: true,
    disablePadding: false,
    label: "businessName",
  },
  { id: "title", numeric: true, disablePadding: false, label: "title" },
  { id: "status", numeric: true, disablePadding: false, label: "status" },
];

interface EnhancedTableProps {
  classes: ReturnType<typeof useStyles>;
  numSelected: number;
  onRequestSort: (
      event: React.MouseEvent<unknown>,
      property: keyof Data
  ) => void;
  onSelectAllClick: (event: React.ChangeEvent<HTMLInputElement>) => void;
  order: Order;
  orderBy: string;
  rowCount: number;
}

function EnhancedTableHead(props: EnhancedTableProps) {
  const {
    classes,
    onSelectAllClick,
    order,
    orderBy,
    numSelected,
    rowCount,
    onRequestSort,
  } = props;
  const createSortHandler =
      (property: keyof Data) => (event: React.MouseEvent<unknown>) => {
        onRequestSort(event, property);
      };

  return (
      <TableHead>
        <TableRow>
          <TableCell padding="checkbox">
            <Checkbox
                indeterminate={
                  numSelected > 0 && numSelected < rowCount
                }
                checked={rowCount > 0 && numSelected === rowCount}
                onChange={onSelectAllClick}
                inputProps={{ "aria-label": "select all desserts" }}
            />
          </TableCell>
          {headCells.map((headCell) => (
              <TableCell
                  key={headCell.id}
                  align={headCell.numeric ? "right" : "left"}
                  padding={headCell.disablePadding ? "none" : "normal"}
                  sortDirection={orderBy === headCell.id ? order : false}
              >
                <TableSortLabel
                    active={orderBy === headCell.id}
                    direction={orderBy === headCell.id ? order : "asc"}
                    onClick={createSortHandler(headCell.id)}
                >
                  {headCell.label}
                  {orderBy === headCell.id ? (
                      <span className={classes.visuallyHidden}>
                                    {order === "desc"
                                        ? "sorted descending"
                                        : "sorted ascending"}
                                </span>
                  ) : null}
                </TableSortLabel>
              </TableCell>
          ))}
        </TableRow>
      </TableHead>
  );
}

const useToolbarStyles = makeStyles((theme: Theme) =>
    createStyles({
      root: {
        paddingLeft: theme.spacing(2),
        paddingRight: theme.spacing(1),
      },
      highlight:
          theme.palette.type === "light"
              ? {
                color: theme.palette.secondary.main,
                backgroundColor: lighten(
                    theme.palette.secondary.light,
                    0.85
                ),
              }
              : {
                color: theme.palette.text.primary,
                backgroundColor: theme.palette.secondary.dark,
              },
      title: {
        flex: "1 1 100%",
      },
    })
);

interface EnhancedTableToolbarProps {
  numSelected: number;
  onClick: (e: React.MouseEvent<unknown>) => void;
}

const EnhancedTableToolbar = (props: EnhancedTableToolbarProps) => {
  const classes = useToolbarStyles();
  const { numSelected } = props;

  return (
      <Toolbar
          className={clsx(classes.root, {
            [classes.highlight]: numSelected > 0,
          })}
      >
        {numSelected > 0 ? (
            <Typography
                className={classes.title}
                color="inherit"
                variant="subtitle1"
                component="div"
            >
              {numSelected} selected
            </Typography>
        ) : (
            <Typography
                className={classes.title}
                variant="h6"
                id="tableTitle"
                component="div"
            >
              Customers
            </Typography>
        )}
        {numSelected > 0 ? (
            <Tooltip title="Delete">
              <IconButton aria-label="delete" onClick={props.onClick}>
                <DeleteIcon />
              </IconButton>
            </Tooltip>
        ) : (
            <Tooltip title="Filter list">
              <IconButton aria-label="filter list">
                <FilterListIcon />
              </IconButton>
            </Tooltip>
        )}
      </Toolbar>
  );
};

const useStyles = makeStyles((theme: Theme) =>
    createStyles({
      root: {
        width: "100%",
      },
      paper: {
        width: "100%",
        marginBottom: theme.spacing(2),
      },
      table: {
        minWidth: 750,
      },
      visuallyHidden: {
        border: 0,
        clip: "rect(0 0 0 0)",
        height: 1,
        margin: -1,
        overflow: "hidden",
        padding: 0,
        position: "absolute",
        top: 20,
        width: 1,
      },
    })
);

export default function BusinessCustomersTable() {
  const classes = useStyles();
  const [order, setOrder] = React.useState<Order>("asc");
  const [orderBy, setOrderBy] = React.useState<keyof Data>("businessName");
  const [selected, setSelected] = React.useState<number[]>([]);
  const [page, setPage] = React.useState(0);
  const [rowsPerPage, setRowsPerPage] = React.useState(5);
  const [rows, setRows] = useState<Data[]>([]);
  const [loading, setLoading] = useState(false);

  let updatedState: Data[] = [];

  // TODO - move this to API file
  const apiUrl = "http://185.185.126.15:8080/api/management/onboarding/task";

  useEffect(() => {
    const getData = async () => {
      setLoading(true);

      getTask(1, 100)
          .then((resp) => {
            console.log(resp.data);
          })
          .catch((error) => {
            console.error(error);
          });
      const response = await axios.get(apiUrl, {
        params: { page: 1, size: 100 },
      });
      setLoading(false);

      const objContent: any = response.data.content;

      for (let a = 0; a < objContent.length; a++) {
        updatedState[a] = createData(
            objContent[a].id,
            objContent[a].businessName,
            objContent[a].title,
            objContent[a].status
        );

        setRows([...rows, ...updatedState]);
      }
    };

    getData();
  }, []);

  const handleRequestSort = (
      event: React.MouseEvent<unknown>,
      property: keyof Data
  ) => {
    const isAsc = orderBy === property && order === "asc";
    setOrder(isAsc ? "desc" : "asc");
    setOrderBy(property);
  };

  const handleSelectAllClick = (
      event: React.ChangeEvent<HTMLInputElement>
  ) => {
    if (event.target.checked) {
      const newSelecteds = rows.map((n) => n.id);
      setSelected(newSelecteds);
      return;
    }
    setSelected([]);
  };

  const handleClick = (event: React.MouseEvent<unknown>, id: number) => {
    const selectedIndex = selected.indexOf(id);
    let newSelected: number[] = [];

    if (selectedIndex === -1) {
      newSelected = newSelected.concat(selected, id);
    } else if (selectedIndex === 0) {
      newSelected = newSelected.concat(selected.slice(1));
    } else if (selectedIndex === selected.length - 1) {
      newSelected = newSelected.concat(selected.slice(0, -1));
    } else if (selectedIndex > 0) {
      newSelected = newSelected.concat(
          selected.slice(0, selectedIndex),
          selected.slice(selectedIndex + 1)
      );
    }

    setSelected(newSelected);
  };

  const handleChangePage = (event: unknown, newPage: number) => {
    setPage(newPage);
  };

  const handleChangeRowsPerPage = (
      event: React.ChangeEvent<HTMLInputElement>
  ) => {
    setRowsPerPage(parseInt(event.target.value, 10));
    setPage(0);
  };

  const handleDeleteClick = async () => {
    // npm install qs
    var qs = require("qs");

    const response = await axios.delete(apiUrl, {
      params: {
        ids: selected,
      },
      paramsSerializer: (params) => {
        return qs.stringify(params);
      },
    });

    if (response.status === 204) {
      const updatedData = rows.filter(
          (row) => !selected.includes(row.id)
      ); // It'll return all data except selected ones

      setRows(updatedData); // reset rows to display in table.
    }
  };

  const isSelected = (id: number) => selected.indexOf(id) !== -1;

  const emptyRows =
      rowsPerPage - Math.min(rowsPerPage, rows.length - page * rowsPerPage);

  return (
      <div className={classes.root}>
        <Paper className={classes.paper}>
          <EnhancedTableToolbar
              numSelected={selected.length}
              onClick={handleDeleteClick}
          />
          <TableContainer>
            <Table
                className={classes.table}
                aria-labelledby="tableTitle"
                aria-label="enhanced table"
            >
              <EnhancedTableHead
                  classes={classes}
                  numSelected={selected.length}
                  order={order}
                  orderBy={orderBy}
                  onSelectAllClick={handleSelectAllClick}
                  onRequestSort={handleRequestSort}
                  rowCount={rows.length}
              />
              <TableBody>
                {loading ? (
                    <div className="spinerr">
                      <CircularProgress />
                    </div>
                ) : null}
                {stableSort(rows, getComparator(order, orderBy))
                    .slice(
                        page * rowsPerPage,
                        page * rowsPerPage + rowsPerPage
                    )
                    .map((row, index) => {
                      const isItemSelected = isSelected(row.id);
                      const labelId = `enhanced-table-checkbox-${index}`;

                      return (
                          <TableRow
                              hover
                              onClick={(event) =>
                                  handleClick(event, row.id)
                              }
                              role="checkbox"
                              aria-checked={isItemSelected}
                              tabIndex={-1}
                              key={row.businessName}
                              selected={isItemSelected}
                          >
                            <TableCell padding="checkbox">
                              <Checkbox
                                  checked={isItemSelected}
                                  inputProps={{
                                    "aria-labelledby":
                                    labelId,
                                  }}
                              />
                            </TableCell>
                            <TableCell
                                component="th"
                                id={labelId}
                                scope="row"
                                padding="none"
                            >
                              {row.id}
                            </TableCell>
                            <TableCell align="right">
                              {row.businessName}
                            </TableCell>
                            <TableCell align="right">
                              {row.title}
                            </TableCell>
                            <TableCell align="right">
                              {row.status}
                            </TableCell>
                          </TableRow>
                      );
                    })}
                {emptyRows > 0 && (
                    <TableRow style={{ height: 53 * emptyRows }}>
                      <TableCell colSpan={6} />
                    </TableRow>
                )}
              </TableBody>
            </Table>
          </TableContainer>
          <TablePagination
              rowsPerPageOptions={[5, 10, 25]}
              component="div"
              count={rows.length}
              rowsPerPage={rowsPerPage}
              page={page}
              onPageChange={handleChangePage}
              onRowsPerPageChange={handleChangeRowsPerPage}
          />
        </Paper>
      </div>
  );
}

Sandbox: https://stackblitz.com/edit/react-ts-tnpk85?file=Hello.tsx

When data is loaded first time and I switch pages I don’t see additional requests to Back end. Looks like data table rows data is loaded only once. I need to implement a lazy pagination and load current page data when I switch page. Do you know how I can fix this?


Get this bounty!!!

#StackBounty: #reactjs #react-select How to load React-select Async with initial options for edit item function when using a search API?

Bounty: 100

I’m using React-Select’s Async Select to implement a dropdown where the user will be able to search through a large amount of data. They will see the employee’s name when they’re searching and they can select multiple employees.

React-select will get the data by using a search API that I have and will insert the query behind by using this method:

const loadOptions = async () => {
        const res = await fetch(`https://example.com/search_user?value=${query}`);
        const data = await res.json();
        const results = data.Result;

        if (results === 'No result found.') {
            return [];
        }

        return results;
    };

What I want to get is the employee ID and their full name, then post both of these info to an API after the user submitted the form.

All these work perfectly but now I want to implement an edit function where the user can edit the Group Members that have been saved into the DB and created.

So naturally I will need to load the react-select‘s Async dropdown with the initial options, but I have no idea how to load the initial options when I’m using a search API that only will get a certain list of results after I typed something.

I want something like this when the user clicks on edit, which will show the saved employee’s name (could be single or multi options selected) from the DB:
enter image description here

Here is the codesandbox for reference but I’m only using a fake API link:

Edit strange-resonance-l631s

Edit:

For @Pitter’s reference:
enter image description here


Get this bounty!!!

#StackBounty: #javascript #reactjs #aws-amplify React select input value is not resetting on page refresh

Bounty: 100

FWIW, I’m using Gatsby deployed with AWS Amplify.

I have a simple language switcher on my website. When I open the site in a new tab, or in private browsing, the behavior is as expected. It currently defaults to english and you can switch to German. However when I just do a simple refresh, the translatable text on the page goes back to english, while what’s shown in the select is "German". What’s most curious, however, is that inspecting the select looks like the attached picture. While on the page it shows German selected, in the HTML the English option has selected="". Which is correct, but why does it show German?

  const changeLanguage = (lang) => {
    i18n.changeLanguage(lang);
  };
...
 <select
  value={i18n.language}
  onChange={(event) => changeLanguage(event.target.value)}
              

language switcher


Get this bounty!!!

#StackBounty: #javascript #reactjs #typescript React type error "not assignable to parameter of type 'never'"

Bounty: 50

What I want to do is loop over current posttype and ‘products’ but I’m always struggle with the types. So I got the following error:

Argument of type ‘Record<string, any>[]’ is not assignable to parameter of type ‘never’.

On part this part:

...pages.map((page) => ({

Here is my code:

    const pages = useSelect((select) => {
    const editor = select('core/editor');
    const currentPostType: string = editor.getCurrentPostType();
    const selectablePostTypes = [currentPostType, 'products'];

    const postList = [];

    selectablePostTypes.forEach((singlePostType) => {
      const records: Record<string, any>[] = select('core').getEntityRecords(
        'postType',
        singlePostType,
      );

      postList.push(records);
    });
  });

  // Make dropdown of pagesOptions
  const pagesOptions = [
    ...[
      {
        value: '0',
        label: __('No page selected', 'demosite'),
      },
    ],
    ...pages.map((page) => ({
      value: page.id,
      label: page.title,
    })),
  ];

Add the code part which works:

      const pages = useSelect((select) => {
    const editor = select('core/editor');
    const postType: string = editor.getCurrentPostType();
    const records: Record<string, any>[] = select('core').getEntityRecords(
      'postType',
      postType,
    );

    return records ?
      records.map((record) => ({
        description: record.description,
        id: record.id,
        featuredMedia: record.featuredMedia,
        link: record.link,
        subtitle: record.subtitle,
        title: record.title.rendered,
      })) :
      [];
  });

But here it is targeted on one post type, the post-type your editing right now. But I want to loop this over some posttypes.

examples:

const selectablePostTypes = ['page', 'post', 'product'];


Get this bounty!!!

#StackBounty: #javascript #html #css #reactjs #image How to load image from shared file on local server?

Bounty: 100

I am receiving data from local server backend and I want to be to load image. I am receiving array of object like this:

[ {
        "t_pdno": "SFC093989",
        "t_mitm": "SLS005251ACL-3382012763-1",
        "t_qrdr": 60,
        "Operations": "10,20,30,40,60,70",
        "path": "\\192.168.1.245\Images\ACL-3382012763-1.jpg"
    },
    {
        "t_pdno": "SFC093991",
        "t_mitm": "SLS005251ACL-3382012765-1",
        "t_qrdr": 120,
        "Operations": "10,20,30,40",
        "path": "\\192.168.1.245\Images\ACL-3382012765-1.jpg"
    },]

After I console.log(rowData.path) the path it looks like this:

\192.168.1.245ImagesACL-3382014766-1.jpg

So it is perfect to paste in browser and I get the image:

enter image description here

The problem is I cannot load it in my img tag. I tried:

<img
    src={process.env.PUBLIC_URL + rowData.path}
    alt={`${rowData.t_mitm}`}
     loading="lazy"
              />

<img
     src={rowData.path}
     alt={`${rowData.t_mitm}`}
     loading="lazy"
              />




  <img
      src={require(rowData.path)}
      alt={`${rowData.t_mitm}`}
      loading="lazy"
                  />



 <img
      src={`url(rowData.path)`}
      alt={`${rowData.t_mitm}`}
      loading="lazy"
                  />

and nothing is working. How can I load the images?

UPDATE:

If I install http-server to the \192.168.1.245 server and host the Images folder there on specific port I am able to receive the image. But this mean that I will always have to keep folder hosted.

UPDATE 2:

If I try loading the image like this:

   <img
          src={`file://192.168.1.245/Images/${rowData.t_mitm}`}
          alt={`${rowData.t_mitm}`}
          loading="lazy"
                      />

It probably works but I get:

Not allowed to load local resource:
file://192.168.1.245/Images/ACL-3382012763-1.jpg


Get this bounty!!!

#StackBounty: #javascript #reactjs #react-router Dynamically adding active class to multi level sidebar menu in REACT JS

Bounty: 50

I’m currently creating a multilevel sidebar menu in REACT and I want to dynamically add active class to parent <li> when the current route is active.

The code below is how I do it for a single menu.

<Route
  path={this.props.to}
  exact={this.props.activeOnlyWhenExact}
  children={({ match }) => (
    <li className={match ? "active" : ""}>
      <Link to={this.props.to} activeClassName="active">
        <span className="sidebar-mini-icon">
          <i className={this.props.icon}></i></span>
          {this.props.label}
      </Link>
    </li>
  )}
/>

Now I wonder how I do it with multilevel menu. My menu looks like this;

enter image description here

As you can see, only the menu has the active class. Here’s my code for the multi level menu:

<Route
  path={this.props.to}
  exact={this.props.activeOnlyWhenExact}
  children={({ match }) => (
  <li>
    <a data-toggle="collapse" href="#Collapse">
      <span className="sidebar-mini-icon">
        <i className={this.props.icon}></i>
      </span>
      <span className="sidebar-normal">
        {this.props.name}
        <b className="caret"></b>
      </span>
    </a>
    <div class="collapse" id="Collapse">
       <ul className="nav">
         {this.props.children}
       </ul>
    </div>
  </li>
  )}
/>

I’m using React Router. Any help will be appreciated. Thank you.


Get this bounty!!!

#StackBounty: #reactjs Standard way of fetching external data before showing webpage (2021 September)

Bounty: 100

I like the way in AngularJS of fetching external data before showing webpage. The data will be sent one by one to the frontend before showing the webpage. We are certain that the website and the data on it is good when we see it.

$stateProvider
    .state('kpi', {
        url:'/kpi',
        templateUrl: '/htmls/kpi.html',
        resolve: {
            getUser: ['lazy', 'auth', function (lazy, auth) { return auth.getUser() }],
            postPromise: ['posts', 'getUser', function (posts, getUser) { return posts.getAll() }],
            userPromise: ['users', 'postPromise', function (users, postPromise) { return users.getAll() }],
            logs: ['kpiService', 'userPromise', function (kpiService, userPromise) { return kpiService.getLogs() }],
            subscribers: ['kpiService', 'logs', function (kpiService, logs) { return kpiService.getSubscribers() }]
        },
        controller: 'KpiCtrl'
    })

Now, I would like to achieve this in ReactJS, I tried:

class Kpi extends React.Component {
    state = { logs: [] };

    getChartOptions1 () {
       // this.state.logs is used
    }

    componentDidMount() {
        axios.get(`${BACKEND_URL}/httpOnly/kpi/logs`).then(
            logs => {
              this.setState({logs.data});
            });
    };

    render() {
        return 
            <div>
                <HighchartsReact highcharts={Highcharts} options={this.getChartOptions1()} {...this.props} />
                <div>{JSON.stringify(this.state.logs)}</div>
            </div>
    }
}

But it seems that, it first called getChartOptions1 with unready data, rendered the webpage, then fetched the external data, then called again getChartOptions1 with ready data, rendered the webpage again.

I don’t like the fact that getChartOptions was called twice (first with unready data), and the page was rendered twice.

There are several ways discussed: Hooks, React.Suspense, React.Lazy, etc. Does anyone know what’s the standard way of fetching external data before showing the webpage in React?


Get this bounty!!!

#StackBounty: #javascript #reactjs #next.js #antd #react-state-management cart count is not getting updated properly

Bounty: 50

screeen record of the issue: https://streamable.com/ofn42v

it is working fine in local but once deployed to production(vercel), it is not working. i have tried sooo many different things like having a separate state in cart, useEffect with totalQuantity in dependency array and nothing seems to work. Ideally when the totalQuantity inside the context is updated, the components using it should rerender as mentioned in react doc which is happening from n to 2 except for 1. can someone please help 🙁

my code for the cart icon in nav bar:

function Cart(props) {
  const { enableCart, totalQuantity } = useContext(AppContext);

  return (
    <>
      {enableCart ? (
        <Link href="/cart" passHref>
          <a aria-label="Shopping cart" title="Shopping cart">
            <Badge count={totalQuantity} offset={[0, 5]}>
              <ShoppingCartIcon className="w-7 h-7" />
            </Badge>
          </a>
        </Link>
      ) : null}
    </>
  );
}

Update quantity – code in appContext:

import { useCookies } from "react-cookie";
export const AppProvider = (props) => {

  const [cartItems, updateCart] = useState([]);
  const [totalQuantity, setTotalQuantity] = useState(0);
  const [cookies, setCookie] = useCookies(["cart"]);
  const cookieCart = cookies.cart;

  useEffect(() => {
    cartOperations();
  }, []);

  const calculateAmountQuantity = (items) => {
    let totalCount = 0;
    let totalPrice = 0;
    items.forEach((item) => {
      totalCount += item.quantity;
      totalPrice += item.price * item.quantity;
      setTotalAmount(totalPrice);
      setTotalQuantity(totalCount);
    });
  };

  const cartOperations = async (items) => {
    if (items !== undefined) {
      updateCart([...items]);
      calculateAmountQuantity(items);
    } else if (cookieCart !== undefined) {
      updateCart([...cookieCart]);
      calculateAmountQuantity(cookieCart);
    } else {
      updateCart([]);
      setTotalAmount(0);
      setTotalQuantity(0);
    }
  };
  
  const addItem = (item) => {
    let items = cartItems;
    let existingItem;
    if (items) existingItem = items.find((i) => i.id === item.id);

    if (!existingItem) {
      items = [
        ...(items || []),
        Object.assign({}, item, {
          quantity: 1,
        }),
      ];
      updateCart([...items]);
      setTotalAmount(totalAmount + item.price * 1);
      setTotalQuantity(totalQuantity + 1);
    } else {
      const index = items.findIndex((i) => i.id === item.id);
      items[index] = Object.assign({}, item, {
        quantity: existingItem.quantity + 1,
      });
      updateCart([...items]);
      setTotalAmount(totalAmount + existingItem.price);
      setTotalQuantity(totalQuantity + 1);
    }
    saveCartToCookie(items);
    saveCartToStrapi(items);
  };

i am storing the cart content in cookie.

code for AppContext is here in github, full nav bar code

Live url: https://sunfabb.com

Goto Products, add few items to cart, then try removing one by one from the cart page. (i have enabled react profiler in prod as well)


Get this bounty!!!