ReactJS template literal as variable name - javascript

I'm wondering if it's possible to evaluate a templateLiteral as a variable name and then use it to reference an import.
So far this is my code:
//These are redux action types
import * as fileTypeOneActions from "./fileTypeOne/actionTypes"
import * as fileTypeTwoActions from "./fileTypeTwo/actionTypes"
import * as fileTypeThreeActions from "./fileTypeThree/actionTypes"
export const updateApiResponse = (reducer, apiResponse) => {
//reducer can be"fileTypeOne", "fileTypeTwo", or "fileTypeThree" as arguments
const reducerKey = eval(`${reducer}Actions`)
return {
type: reducerKey.UPDATE_API_RESPONSE,
apiResponse: apiResponse,
}
}
Based on the string passed as 'reducer' argument, I want to reference the correct import and return correct redux action type as such. Any ideas?
the eval statement above is not working.

//These are redux action types
import * as fileTypeOneActions from "./fileTypeOne/actionTypes"
import * as fileTypeTwoActions from "./fileTypeTwo/actionTypes"
import * as fileTypeThreeActions from "./fileTypeThree/actionTypes"
const reducerMap = {
1:fileTypeOneActions,
2:fileTypeTwoActions,
3:fileTypeThreeActions
}
export const updateApiResponse = (reducer, apiResponse) => {
//reducer can be"fileTypeOne", "fileTypeTwo", or "fileTypeThree" as arguments
//const reducerKey = eval(`${reducer}Actions`)
const reducerKey = reducerMap(reducer) // reducer can be 1,2,3 or any other key
return {
type: reducerKey.UPDATE_API_RESPONSE,
apiResponse: apiResponse,
}
}
try to avoid eval. It's too dangerous.

Slightly different version that maps better to the input you have:
//These are redux action types
import * as fileTypeOneActions from "./fileTypeOne/actionTypes"
import * as fileTypeTwoActions from "./fileTypeTwo/actionTypes"
import * as fileTypeThreeActions from "./fileTypeThree/actionTypes"
const actions = {
fileTypeOneActions,
fileTypeTwoActions,
fileTypeThreeActions
};
export const updateApiResponse = (reducer, apiResponse) => {
const reducerKey = actions[`${reducer}Actions`];
return {
type: reducerKey.UPDATE_API_RESPONSE,
apiResponse: apiResponse,
}
}
Using an object as a map is the way to go. I don't see how this isn't dynamic (as claimed in your comment to the other answer).

Related

Redux abstraction level in selectors with typescript

this is the default implementation of redux with typescript using the connect method
import { connect, ConnectedProps } from 'react-redux'
interface RootState {
isOn: boolean
}
const mapState = (state: RootState) => ({
isOn: state.isOn,
})
const mapDispatch = {
toggleOn: () => ({ type: 'TOGGLE_IS_ON' }),
}
const connector = connect(mapState, mapDispatch)
// The inferred type will look like:
// {isOn: boolean, toggleOn: () => void}
type PropsFromRedux = ConnectedProps<typeof connector>
I want to have reusable selectors from different components I have my selectors as functions seperately rather than in an object mapState like in the example.
So my selectors in a simplified version would be something like:
selectors.js:
const selector1 = (state) => state.attr1;
const selector2 = (state) => state.attr2;
const selector3 = (state) => state.attr3;
I want to create a function that accepts an object
eg
import {selector1,selector2} from '../../selectors';
const selectors = {
attr1:selector1,
attr3:selector3,
}
pass it through a wrapper function and then that function to generate the mapState
I have successfully done that
const mapStateToProps = (selectors: Selectors) => {
return Object.keys(selectors).reduce((acc, selectorKey) => {
acc[selectorKey] = selectors[selectorKey](state);
return acc;
}, {});
};
so I can use it like this const connector =connect(mapStateToProps(selectors),actions)
but the generated output doesn't have any types. Is this feasible?
Redux suggests to retrieve the props from type PropsFromRedux = ConnectedProps<typeof connector> but in my case only the types of actions appear
Per our docs, you should avoid using connect for any new code today. Instead, you should use the React-Redux hooks API (useSelector and useDispatch).
One of the major reasons for this is that the hooks are much easier to use with TypeScript.
See our guidelines for setting up "pre-typed" hooks for use in your app code.

React: Parsing error: Identifier 'publicKey' has already been declared

I'm new to React so please bear with my naming conventions:
How can I use the same name of 2 props from two different 3rd party components?
import { useWallet} from "#solana/wallet-adapter-react";
import { useMoralis } from "react-moralis";
export const SendOneLamportToRandomAddress = () => {
const { publicKey, sendTransaction } = useWallet();
const { publicKey } = useMoralis();
}
It looks as if {publicKey as pK} won't work and neither does {pK : publicKey}
What am I missing?
thanks

Reading JSON file into React Context Provider with Typescript

My React Typescript app has a Context Provider DataProvider that is to read a value from a JSON file and provide it in the Context useData(). I am trying to do the read synchronously to avoid having to deal with a isLoading since this is a tiny local JSON file.
Is there a recommended way to read the JSON file synchronously inside a Context Provider?
I tried the following using node-sync, but its giving a Typescript error
Object is possibly 'undefined'.ts(2532)
on data at line
return data.find(...
Tried changing it to
return data?.find(...`
but now the error is
Property 'find' does not exist on type 'never'.ts(2339)
import React, {
createContext,
useContext,
Consumer,
Context,
ReactNode,
useMemo,
} from 'react';
import Sync from 'sync';
export interface DataProviderProps {
children: ReactNode;
}
export interface Data {
secretNumber?: string;
}
// #ts-ignore
const DataContext: Context<Data> = createContext<Data>();
export function DataProvider({ children }: DataProviderProps) {
const secretNumber = useMemo(() => {
// Read from JSON file
const contractFile =
process.env.REACT_APP_WORLD === 'main'
? '../main.json'
: '../test.json';
let data;
Sync(function () {
data = import(contractFile);
});
return data.find( // <=== TS error: Object is possibly 'undefined'. ts(2532)
({ name }: { name: string }) => name === 'elonmusk',
)?.secretNumber;
}, []);
const states = useMemo<Data>(() => {
return {
secretNumber,
};
}, [secretNumber]);
return (
<DataContext.Provider value={states}>
{children}
</DataContext.Provider>
);
}
export function useData(): Data {
return useContext(DataContext);
}
export const DataConsumer: Consumer<Data> = DataContext.Consumer;
array.find() returns undefined If no values satisfy the testing function, from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find, so just add (!) after the array.find()! fxn to ascertain a value would be returned.
sample code stub
data.find(todoCodeStubhere)!

How to use two different slices with Redux-toolkit?

I'm trying to fetch data from two different API's using Redux-toolkit, however I don't want them to be fetched simultaneously. Let's say I have two buttons and if I click on the button 1 the app should fetch data from the first api and if the click is on the button 2 the data should come from the second API.
Other thing is that the API's have different structures, so I need two different slices (or reducers). The issue is, since I'm using the same store for both reducers, both API's are being fetched.
import { configureStore, ThunkAction, Action } from '#reduxjs/toolkit'
import footballReducer from 'features/tournaments/footballSlice'
import volleyballReducer from 'features/tournaments/tournamentsSlice'
export const store = configureStore({
reducer: {
matchesFootball: footballReducer, // USED TO FETCH API 1
matchesVolleyball: volleyballReducer, // USED TO FETCH API 2
}
})
export type AppDispatch = typeof store.dispatch
export type RootState = ReturnType<typeof store.getState>
export type AppThunk<ReturnType = void> = ThunkAction<
ReturnType,
RootState,
unknown,
Action<string>
>
Is there a way where I can control which reducer will be executed?
My first thoughts were:
1- Use two different slices, one for each API and execute its respective reducer (I couldn't be sure if this last part is possible)
2- To create two stores, what would make it hard to manage, since I have only two reducers for now, but it'll increase to almost 10;
3- Use only one slice, where I would set one extra reducer for each API data, in that case I believe I would have to create one different function for each fetch;
Is there a builtin way to do that? Or at least a more straightforward way, which wouldn't look like some bad trick?
import { createAsyncThunk, createSlice, PayloadAction } from "#reduxjs/toolkit";
import { RootState } from "store/store";
import http from "services/services";
import IVolleyballModel from "models/VoleyballModel";
export interface VolleyballState {
matches: IVolleyballModel[]
status: "success" | "loading" | "failed"
rounds: number
tournamentName: string
}
const initialState: VolleyballState = {
matches: [],
status: "loading",
rounds: 0,
tournamentName: ''
};
export const fetchMatches = createAsyncThunk(
"matchesList/fetchMatches",
async (gender: number) => {
const response = await http.getSLTable(gender);
return response.data;
}
);
export const tournamentsSlice = createSlice({
name: "matchesList",
initialState,
reducers: {
setTournamentName (state, action: PayloadAction<string>) {
state.tournamentName = action.payload
}
},
extraReducers: (builder) => {
builder
.addCase(fetchMatches.pending, (state) => {
state.status = "loading";
})
.addCase(fetchMatches.fulfilled, (state, action) => {
state.status = "success";
let allMatches: any[] = [];
let rounds: number = 0;
action.payload.grupos[0].rodadas.map((round: { jogos: [] }) => {
// ... SOME LOGIC
});
state.matches = [...allMatches];
state.rounds = rounds;
})
.addCase(fetchMatches.rejected, (state) => {
state.status = "failed";
});
},
});
export const { setTournamentName } = tournamentsSlice.actions
export const getData = (state: RootState) => state.matchesVolleyball;
export default tournamentsSlice.reducer;
You can totally do 1. - an extraReducer for one asyncThunk will not trigger for another asyncThunk.
That said, you might also want to explore RTK-Query, which abstracts all that fetching, state keeping and storing logic away from you.
In both cases, I would recommend you read up on it in Chapters 5, 7 and 8 of the official Redux Essentials tutorial that walks you through the different approaches and shows benefits and drawbacks of both.

using react-intl to translate a message key outside a component

I'm using the react-intl library for internationalization. Inside a component, I use the injectIntl HOC to translate message keys:
import {injectIntl} from 'react-intl';
const Component = props => (
const message = props.intl.formatMessage({id: 'message.key'});
// remainder of component omitted
);
export default injectIntl(Component);
Is it possible to get a message translation if I'm not inside a component?
Yes it is!
You have to setup you application to provide the intl object so that you can use it from outside react components. You will have to use the imperative API for these cases. You can do something like this:
import { IntlProvider, addLocaleData, defineMessages } from 'react-intl';
import localeDataDE from 'react-intl/locale-data/de';
import localeDataEN from 'react-intl/locale-data/en';
import Locale from '../../../../utils/locale';
addLocaleData([...localeDataEN, ...localeDataDE]);
const locale = Locale.getLocale(); // returns 'en' or 'de' in my case
const intlProvider = new IntlProvider({ locale, messages });
const { intl } = intlProvider.getChildContext();
const messages = defineMessages({
foo: {
id: 'bar',
defaultMessage: 'some label'
}
});
const Component = () => (
const componentMessage = intl.formatMessage(messages.foo);
);
I've done a different setup for me, but I guess this should work for you.

Categories

Resources