Use custom middleware for async actions. Actions must be plain objects - javascript

I am new to this, and this error occurs, looked at a lot of solutions on it, nothing helped to fix.
Another mistake in store.js. When point to thunk.
Argument type ThunkMiddleware & {withExtraArgument (extraArgument: E): ThunkMiddleware{}, AnyAction, E>} is not assignable to parameter type Function
Actions.js
export const SET_YEAR = 'SET_YEAR';
export const FETCH_USERS_EXAMPLE = "FETCH_USERS_EXAMPLE";
export function setYear(year) {
return {
type: 'SET_YEAR',
payload: year,
}
}
export async function getFreeData() {
try {
return async (dispatch) => {
let res = await fetch(`https://jsonplaceholder.typicode.com/users`);
let userList = await res.json();
dispatch({
type: "FETCH_USERS_EXAMPLE",
payload: userList
});
return userList;
}
} catch (e) {
console.log("Error", e);
}
}
Reducer.js
import {SET_YEAR, FETCH_USERS_EXAMPLE} from '../Actions/TestAction';
export function testReducer(state ={year: ''}, action) {
switch (action.type) {
case 'SET_YEAR':
return {...state, year: action.payload};
case 'FETCH_USERS_EXAMPLE':
return {...state, userList: action.payload};
default:
return state
}
}
Container.js
import TestComponent from "./TestComponent";
import {setYear, getFreeData} from "../../Redux/Actions/TestAction";
import {connect} from "react-redux";
import React from "react";
const mapStateToProps = (store) => ({
items: store.user,
userList: store.page.userList,
year: store.page.year
});
const mapDispatchToProps = {
setYear,
getFreeData
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(TestComponent);
Store.js
import {createStore, applyMiddleware} from 'redux'
import {rootReducer} from './Reducers/rootReducer';
import thunk from 'redux-thunk';
import logger from "redux-logger";
export const store = createStore(rootReducer, applyMiddleware(thunk, logger));
webpack
const path = require("path");
module.exports = {
entry: ['babel-polyfill', "./src/index.js"],
mode: "development",
output: {
filename: "./main.js"
},
devServer: {
contentBase: path.join(__dirname, "dist"),
compress: true,
port: 3000,
watchContentBase: true,
progress: true
},
devtool: "source-map",
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: "babel-loader"
}
},
{
test: /\.html$/,
use: ['raw-loader']
},
{
test: /\.scss$/,
use: [
{
loader: "style-loader"
},
{
loader: "css-loader"
},
{
loader: "sass-loader"
}
]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: ["file-loader"]
}
]
}
};

Your getFreeData function should not be async or it will return a promise.
Instead, return the function for redux-thunk:
export function getFreeData() {
return async (dispatch) => {
try {
let res = await fetch(`https://jsonplaceholder.typicode.com/users`);
let userList = await res.json();
dispatch({
type: "FETCH_USERS_EXAMPLE",
payload: userList
});
return userList;
}
catch (e) {
console.log("Error", e);
}
}
}
Hope this helps.

Related

React component not rendering correctly after await call

i have html template i purchased at themeforest.net im trying to convert it to webpack and react every thing works fine now but there is weird bug when ever i make await call in componentDidMount the page didnt render correctly
let res = await axios({
method: "get",
url: "http://localhost:8080/api/main/mainProducts",
});
this.setState({ allProducts: res.data });
when i comment the await call
and setstate to static data
let allProducts = [
{
//array data
},
]
this.setState({ allProducts: allProducts });
its render correctly. here is the component
export class MainComponent extends React.Component<{}, any> {
constructor(props) {
super(props);
this.state = {
allProducts: [],
};
}
async componentDidMount() {
let res = await axios({
method: "get",
url: "http://localhost:8080/api/main/mainProducts",
});
this.setState({ allProducts: res.data });
}
render(): any {
return (
<div className="product-4 product-m no-arrow">
{this.state.allProducts.map((x) => (
<div key={x.id} className="product-box product-wrap">
<div className="img-wrapper">
<div className="lable-block">
<span className="lable3">new</span>{" "}
<span className="lable4">on sale</span>
</div>
<div className="front">
<a href={"/product/" + x.id}>
{x.images.map((image) =>
image.main.toString() === "true" ? (
<img
className="img-fluid blur-up lazyload bg-img"
key={image.id}
src={`/public/productImages/${x.id.toString()}/${
image.imageName
}`}
></img>
) : (
""
)
)}
</a>
</div>
</div>
</div>
))}
</div>
// long html lines here
// ...
);
}
}
webpack config
const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");
const webpack = require("webpack");
module.exports = {
entry: path.join(__dirname, "src/index.tsx"),
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.scss$/,
use: ["style-loader", "css-loader", "sass-loader"],
},
{
test: /\.(svg|gif|jpg|png|eot|woff|woff2|ttf)$/,
use: [
'url-loader',
],
},
{
test: /\.js$/,
exclude: /node_modules/,
use: ["babel-loader"],
},
{
test: /\.tsx?$/,
loader: "ts-loader",
exclude: /node_modules/,
},
{
test: /\.ts$/,
use: ["ts-loader"],
},
],
},
resolve: {
extensions: [".tsx", ".ts", ".js"],
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "src", "index.html"),
filename: "./index.html",
}),
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
}),
],
output: {
path: __dirname + "/dist/",
filename: "index.js",
publicPath: '/'
},
devServer: {
historyApiFallback: true,
proxy: {
"/api": {
target: "http://localhost:5050",
pathRewrite: { "^/api": "" },
},
},
},
mode: "development",
};
entry point index.tsx
import React from "react";
import { render } from "react-dom";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import { MainComponent } from "./views/user/main/main";
import { Bottom } from "./views/user/layout/bottom";
import { Top } from "./views/user/layout/top";
import "core-js/stable";
import "regenerator-runtime/runtime";
import "./helpers/css"; //css imports of the html template
render(
<Router>
<Switch>
<Route path="/" exact>
<>
<Top />
<MainComponent />
<Bottom />
</>
</Route>
</Switch>
</Router>,
document.getElementById("r")
);
import "./helpers/js"; //js imports of the html template
and most importantly css and js imports
css.tsx
import "/public/assets/css/fontawesome.css";
import "/public/assets/css/slick.css";
import "/public/assets/css/slick-theme.css";
import "/public/assets/css/animate.css";
import "/public/assets/css/themify-icons.css";
import "/public/assets/css/bootstrap.min.css";
import "/public/assets/css/bootstrap4-toggle.min.css";
import "/public/assets/css/jquery.lazyloadxt.spinner.css";
import "/public/assets/css/color5.css";
import "/public/assets/css/site.css";
js.tsx
import "/public/assets/js/jquery-ui.min.js";
import "/public/assets/js/jquery.exitintent.js";
import "/public/assets/js/exit.js";
// #ts-ignore
window.Popper = require("/public/assets/js/popper.min.js");
import "/public/assets/js/slick.js";
import "/public/assets/js/menu.js";
import "/public/assets/js/lazysizes.min.js";
import "/public/assets/js/bootstrap.min.js";
import "/public/assets/js/bootstrap4-toggle.min.js";
import "/public/assets/js/bootstrap-notify.min.js";
import "/public/assets/js/fly-cart.js";
import "/public/assets/js/script.js";
import "/public/scripts/layout.js";
i suspect its something to do with slick library
ive tried import() slick js and css files after setstate but it doesnt work
this.setState({ allProducts: res.data }, () => {
import("../../../../public/assets/css/slick.css");
import("../../../../public/assets/css/slick-theme.css");
import("../../../../public/assets/js/slick.js");
});
i have also tried re import the whole css and js files but also doesnt work
i have also tried many other things like using ajax instead of axios but it give the exact same bug ive also tried this
axios({
method: "get",
url: "http://localhost:8080/api/main/mainProducts",
}).then((x) => {
this.setState({ allProducts: x.data });
});
no await here but its same bug also.
a picture of when its render correctly
wrong render
any help would be really appreciated.
thanks !

VueJS is asking to login every i'm open in new tab

i wanna ask about my code, by the way, i'm really new in vue.js so, i wanna ask about this unusual behaviour i've ever meet.
When i've logged in, and then open the url in new tab, it's ask me to login again.
it's always ask me to do login every i open in new tab. am i missed something?
here is my store.js, auth.js and router.js
store.js
import Vue from "vue";
import Vuex from "vuex";
import auth from "./stores/auth.js";
import user from "./stores/user.js";
Vue.use(Vuex);
const store = new Vuex.Store({
modules: {
auth,
user
},
state: {
admin_id: sessionStorage.getItem("admin_id"),
token: sessionStorage.getItem("token"),
errors: [],
msg: [],
user_lists: [],
role_lists: [],
user_data: [],
show_spinner: false,
},
getters: {
isAuth: state => {
return state.token != "null" && state.token != null;
}
},
mutations: {
SET_TOKEN(state, payload) {
state.token = payload;
},
SET_ADMIN_ID(state, payload) {
state.admin_id = payload;
},
SET_ERRORS(state, payload) {
state.errors = payload;
},
SET_MSG(state, payload) {
state.msg = payload;
},
CLEAR_ERRORS(state) {
state.errors = [];
},
ASSIGN_USER_LIST(state, payload) {
state.user_lists = payload
},
ASSIGN_ROLE_LIST(state, payload) {
state.role_lists = payload
},
GET_USER_DATA(state, payload) {
state.user_data = payload;
},
SHOW_SPINNER(state, payload) {
state.show_spinner = payload;
},
}
});
export default store;
auth.js
import $axios from "../api.js";
const state = () => ({});
const mutations = {};
const actions = {
submitlogin({ commit }, payload) {
return new Promise((resolve, reject) => {
$axios
.post("/auth/login", payload)
.then(response => {
if (response.data.status == "success") {
sessionStorage.setItem("token", response.data.token);
sessionStorage.setItem(
"userdata",
JSON.stringify(response.data.userdata)
);
sessionStorage.setItem('admin_id', response.data.userdata[0].user_id)
commit("SET_TOKEN", sessionStorage.getItem("token"), {
root: true
});
commit("SET_ADMIN_ID", sessionStorage.getItem('admin_id'), {
root: true
});
} else {
commit(
"SET_ERRORS",
{ invalid: response.data.msg },
{ root: true }
);
}
resolve(response.data);
})
.catch(error => {
if (error.response.status == 422) {
commit(
"SET_ERRORS",
{ invalid: error.response.msg },
{
root: true
}
);
}
});
});
},
submitlogout({ commit }) {
return new Promise((resolve, reject) => {
$axios
.post("/auth/logout")
.then(response => {
if (response.data.status == "success") {
sessionStorage.removeItem("token");
sessionStorage.removeItem("userdata");
commit("SET_TOKEN", null, {
root: true
});
}
resolve(response.data);
})
.catch(error => {});
});
},
};
export default {
namespaced: true,
state,
actions,
mutations
};
router.js
import Vue from "vue";
import Router from "vue-router";
import store from "./store.js";
import Home from "./pages/DashboardPage";
import Login from "./pages/Login";
import Logout from "./pages/Logout";
import User from "./pages/User";
import UserList from "./pages/UserList";
import AddUser from "./pages/AddUser";
import EditUser from "./pages/EditUserData";
import Profile from "./pages/Profile";
Vue.use(Router);
//DEFINE ROUTE
const router = new Router({
mode: "history",
linkExactActiveClass: "active",
routes: [
{
path: "/",
name: "home",
component: Home,
meta: {
requiresAuth: true
}
},
{
path: "/user/",
name: "user",
component: User,
meta: {
requiresAuth: true
}
},
{
path: "/user/list",
name: "userlist",
component: UserList,
meta: {
requiresAuth: true
}
},
{
path: "/user/tambah",
name: "adduser",
component: AddUser,
meta: {
requiresAuth: true
}
},
{
path: "/user/edit/:id",
name: "edituser",
component: EditUser,
meta: {
requiresAuth: true
}
},
{
path: "/profile",
name: "edituser",
component: Profile,
meta: {
requiresAuth: true
}
},
{
path: "/login",
name: "login",
component: Login
},
{
path: "/logout",
name: "logout",
component: Logout
}
]
});
//Navigation Guards
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
let auth = store.getters.isAuth;
if (!auth) {
next({
name: "login"
});
} else {
next();
}
} else {
next();
}
});
export default router;
i've tried to use localStorage to store the token, but also it's seems not working.
every feedback or suggestion are appreciated. Thank You.
ps : sorry for bad english...
This is a known issue. Vuex state is not persistent. So you need to install and use
vuex-persistedstate https://www.npmjs.com/package/vuex-persistedstate
I hope this helps. Let me know if you have any questions.
Thanks.

How can I use Vite env variables in vite.config.js?

With the following .env in my Vite project:
# To prevent accidentally leaking env variables to the client, only
# variables prefixed with VITE_ are exposed to your Vite-processed code
VITE_NAME=Wheatgrass
VITE_PORT=8080
How can I use VITE_PORT in my vite.config.js?
You can load the app level env variables and add them to the Node level env variables:
import { defineConfig, loadEnv } from 'vite';
import vue from '#vitejs/plugin-vue';
export default ({ mode }) => {
process.env = {...process.env, ...loadEnv(mode, process.cwd())};
// import.meta.env.VITE_NAME available here with: process.env.VITE_NAME
// import.meta.env.VITE_PORT available here with: process.env.VITE_PORT
return defineConfig({
plugins: [vue()],
server: {
port: parseInt(process.env.VITE_PORT),
},
});
}
If the above solution by #matt doesnt work for you then change the vite.config.ts/ vite.config.js like below
1st Solution
import { defineConfig, loadEnv } from 'vite';
import vue from '#vitejs/plugin-vue';
export default defineConfig(({ mode }) => {
const env = loadEnv(
'mock',
process.cwd(),
''
)
const processEnvValues = {
'process.env': Object.entries(env).reduce(
(prev, [key, val]) => {
return {
...prev,
[key]: val,
}
},
{},
)
}
return {
plugins: [vue()],
define: processEnvValues
}
}
2nd Solution
import { defineConfig, loadEnv } from 'vite';
import vue from '#vitejs/plugin-vue';
export default ({ mode }) => {
process.env = Object.assign(process.env, loadEnv(mode, process.cwd(), ''));
return defineConfig({
plugins: [vue()],
});
}

window is not defined server side rendering(react + express)

If the browser URL is requested, routing works normally.
By the way, location = ${path};
The part that says Component An error occurs when calling react renderToString function.
I'm looking at web pack settings or something, but I can't find the cause. Help!
// App.js
import React, {useState, useEffect} from 'react';
import moment from "moment";
import Header from "./components/header/Header";
import Footer from "./components/footer/Footer";
export default function App() {
const [time, setTime] = useState(null);
useEffect(() => {
console.log('use effect..');
setTime(moment().format('hh:mm:ss a'));
}, []);
return (
<div style={{height:'100%'}}>
<Header/>
<div style={{height:'100%'}}>
<h1>Sample App</h1>
<p>Current time is {time}</p>
</div>
<Footer/>
</div>
)
}
// Header.js
import React from 'react';
export default function Header() {
function goPage(path) {
console.log(`goPage..`);
console.log(window.location); // error
// window.location = path;
}
return (
<header style={{width: '100%', border: '1px dotted black'}}>
<div>
<span style={{padding: '4px'}}>My App |</span>
<span style={{padding: '4px', cursor: 'pointer'}}>Home</span>
<span style={{padding: '4px', cursor: 'pointer'}} onClick={goPage('/about')}>About</span>
</div>
</header>
)
}
server.js
const express = require('express');
const path = require('path');
import React from 'react';
import ReactDOMServer from 'react-dom/server';
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const config = require('../webpack.config.js');
const compiler = webpack(config);
import Router from '../routes/index';
import App from '../src/App';
import Html from "../src/Html";
const expressApp = express();
const port = process.env.PORT || 3000;
if (process.env.NODE_ENV != 'production') {
expressApp.use(webpackDevMiddleware(compiler, {
publicPath: '/'
}));
}
expressApp.use(express.static(path.join(__dirname, '')));
expressApp.get('*', (request, response) => {
console.log('request.. in express');
console.log(Router.match(request));
const component = ReactDOMServer.renderToString(Router.match(request));
const html = ReactDOMServer.renderToStaticMarkup(<Html
title="Sample Title"
description="Isomorphic web application sample"
body={component}
/>);
response.send(`<!DOCTYPE html>` + html);
});
expressApp.listen(port, () => {
console.log(`App is listening at http://localhost:${port}/`);
});
// Router
import React from 'react';
import App from "../src/App";
import NotFound from "../src/components/error/NotFound";
import Error from "../src/components/error/Error";
import About from "../src/components/about/About";
const routes = [
{name : 'home', path : '/', action : () => <App/>},
{name : 'about', path : '/about', action : () => <About/>},
{name : '404', path : '/404', action : () => <NotFound/>},
{name : '500', path : '/500', action : () => <Error/>},
];
export default {
match(location) {
console.log(location.path);
const route = routes.find(x => x.path === location.path);
if (route) {
try {
return route.action();
} catch (err) {
console.log('err');
return routes.find(x => x.path === '/500').action();
}
} else {
console.log('404');
return routes.find(x => x.path === '/404').action();
}
}
}
// webpack config
const path = require('path');
// plugin
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const MomentLocalesPlugin = require('moment-locales-webpack-plugin');
const nodeExternals = require('webpack-node-externals');
// SSR
const ssr = {
mode : 'development',
entry: './server/server.js',
output: {
path: path.join(__dirname, '/build'),
filename: "server.js",
},
target: 'node',
node: false,
externals: [nodeExternals()],
resolve: {
modules: ['node_modules']
},
module: {
rules: [
{
test: /(.js|.jsx)/,
exclude: /node_modules/,
include: [
path.join(__dirname, '/src'),
path.join(__dirname, '/routes/index.js'),
path.join(__dirname, '/server/server.js'),
],
use: {
loader: 'babel-loader'
}
},
]
},
plugins: [
new CleanWebpackPlugin({
cleanAfterEveryBuildPatterns: ['build']
})
],
};
// CSR
const csr = {
mode : 'development',
entry:'./client/client.js',
output: {
publicPath: '/',
path: path.join(__dirname, '/build'),
filename: 'client.js'
},
target: 'web',
module: {
rules: [
{
test: /(.js|.jsx)/,
exclude: /node_modules/,
include: [
path.join(__dirname, '/src'),
path.join(__dirname, '/routes/index.js'),
path.join(__dirname, '/client/client.js'),
],
use: {
loader: 'babel-loader'
}
},
]
},
// devtool: 'source-map',
plugins: [
new MomentLocalesPlugin({
localesToKeep: ['es-us', 'ru'],
}),
]
};
module.exports = [ssr, csr];
The server is composed of express, and is set to babel7 and webpack4.
If you look at the server.js part, you get the corresponding react component in the request URL.
If renderToString() is called on the obtained component, an error of window is not defined occurs.
I really want to solve it. Please help.
Why window is not defined in SSR
On the ssr there is no definition for window. Because window is a browser based property.
you need to do something to avoid that. e.g
function goPage(path) {
console.log(`goPage..`);
if (window) {
console.log(window.location);
}
}
and on the server side you can get the url by this
req.url
// or
app.get('/users/:userId/books/:bookId', function (req, res) {
console.log(req.params)
}

Property 'children' is missing in type

I am trying to setup Storybook with Typescript using babel-loader and ts-loader.
Everything works fine except using children in a React component:
[tsl] ERROR in .../stories/index.stories.tsx(8,56)
TS2769: No overload matches this call.
Property 'children' is missing in type '{ title: string; }' but required in type 'Props'.
This is the .storybook/main.js file:
module.exports = {
addons: [
"#storybook/addon-knobs",
],
stories: ["../packages/**/*.stories.tsx"],
webpackFinal: async config => {
config.module.rules.push({
test: /\.(ts|tsx)$/,
exclude: /node_modules/,
use: [
{
loader: require.resolve('ts-loader')
},
{
loader: require.resolve('babel-loader'),
options: {
presets: [
"#babel/preset-env",
"#babel/preset-react"
]
}
}
],
});
config.resolve.extensions.push('.ts', '.tsx');
return config;
}
};
This is the index.stories.tsx file:
import React from "react";
import Collapsible from "../src";
export default {
title: "Collapsible"
};
const content = <span>Content</span>;
export const simpleCollapsible = () => (
<Collapsible title="Collapsible">{content}</Collapsible>
);
And this is the implementation of Collapsible:
import React, { ReactNode, useState } from "react";
import styled, { ThemeProvider } from "styled-components";
import {
BorderProps,
ColorProps,
FlexboxProps,
LayoutProps,
SpaceProps,
TypographyProps
} from "styled-system";
import Theme from "#atw/theme";
import { KeyboardArrowDown } from "#styled-icons/material";
import Box from '~/box';
import Button from '~/button';
interface Props
extends BorderProps,
ColorProps,
FlexboxProps,
LayoutProps,
SpaceProps,
TypographyProps {
children: ReactNode;
title: string;
}
const Collapsible = ({ children, title, ...rest }: Props) => {
const [isCollapsed, setIsCollapsed] = useState(false);
const handleCollapse = () => {
setIsCollapsed(!isCollapsed);
};
return (
<ThemeProvider theme={Theme}>
<Button
border="none"
padding={0}
marginBottom={2}
width={1}
textAlign="start"
onClick={handleCollapse}
{...rest}
>
<IconBox isCollapsed={isCollapsed}>
<KeyboardArrowDown size="24px" />
</IconBox>
{title}
</Button>
{!isCollapsed && (
<Box display="flex" flexDirection="column">
{children}
</Box>
)}
</ThemeProvider>
);
};
export default Collapsible;
Is there anything here I'm doing wrong?
One possible solution is to leverage the default children mechanism in functional components, React.FC, which lets you mount children without explicitly include them as a prop in your type definition. For your case this is achievable by applying the following changes:
interface Props
extends BorderProps,
ColorProps,
FlexboxProps,
LayoutProps,
SpaceProps,
TypographyProps {
title: string;
}
const Collapsible: React.FC<Props> = ({ children, title, ...rest }) => {
...
};
Working sandbox for this
step 1
add React.FC: it already comes with children declared for you. And add your custom Props inside the React's.
Your code should be this:
const Collapsible : React.FC<Props> = ({ children, title, ...rest }) => {
step 2
interface Props extends BorderProps,
ColorProps,
FlexboxProps,
LayoutProps,
SpaceProps,
TypographyProps {
children: ReactNode; // as React.FC declares it for you, just delete this line
title: string;
}
I added the Babel Preset Typescript and the error is gone.
This is what my .storybook/main.js looks like now:
module.exports = {
addons: [
"#storybook/addon-knobs",
],
stories: ["../packages/**/*.stories.tsx"],
webpackFinal: async config => {
config.module.rules.push({
test: /\.(ts|tsx)$/,
exclude: /node_modules/,
use: [
{
loader: require.resolve('ts-loader'),
options: {
configFile: '../tsconfig.json',
transpileOnly: true
}
},
{
loader: require.resolve('babel-loader'),
options: {
presets: [
"#babel/preset-env",
"#babel/preset-react",
"#babel/preset-typescript"
]
}
}
],
});
config.resolve.extensions.push('.ts', '.tsx');
return config;
}
};

Categories

Resources