I was trying to return data using ES6 function , but it is returning the function instead of result.
The result should be true or false as per my code
My Code
get_gathereddata_status.js
export default () => (dispatch, getState) => {
const { experiment } = getState();
const { selectedTab, gatherData } = experiment.tabs;
const { environmentalChanges: { environmentFactor, environmentLocation } } = experiment;
const { populationChanges: { populationlLocation, populationFactor } } = experiment;
if (selectedTab === 'tab1') {
return environmentFactor !== '' && environmentLocation !== '' && !gatherData[selectedTab];
} else if (selectedTab === 'tab2') {
return populationlLocation !== '' && populationFactor !== '' && !gatherData[selectedTab];
}
return false;
};
mapStateToProps
function mapStateToProps({ experiment }) {
const { selectedTab } = experiment.tabs;
const isGatherDataEnabled = gatherDataStatus();
console.log(isGatherDataEnabled);
return {
selectedTab,
isGatherDataEnabled
};
}
console.log in the mapStateToProps
ƒ (dispatch, getState) {
var _getState = getState(),
experiment = _getState.experiment;
var _experiment$tabs = experiment.tabs,
selectedTab = _experiment$tabs.selectedTab,
get_gathereddata_status.js is returning a function that returns another function:
() => (dispatch, getState) => { //...}
So when you call it with:
const isGatherDataEnabled = gatherDataStatus();
// assuming gatherDataStatus this is the same as get_gathereddata_status.js
isGatherDataEnabled is now the function returned by gatherDataStatus() not the result of your second function.
I think you just want to export the second function:
export default (dispatch, getState) => { //.. }
Alternatively you could call the returned function if you really need it:
console.log(isGatherDataEnabled(dispatch, getState));
// You need to call this with the expected arguments. Where do these come from?
Related
I'm working on a trivia app using ReactjS and every time I try to update my state named "game", I get the following error message:
Uncaught TypeError: game is undefined.
My webapp is structured as follows:
App -> Question -> Answers.
in App:
const [game, setGame] = React.useState([]);
function holdAnswer(qKey) {
console.log(qKey);
setGame((oldGame) => {
oldGame.map((element) => {
return qKey === element.key ? {} : element;
});
});
console.log(qKey);
}
React.useEffect(function () {
console.log("Effect ran");
fetch("https://opentdb.com/api.php?amount=5")
.then((res) => res.json())
.then((data) =>
setGame(
data.results.map(function (element) {
return {
...element,
key: uniqid(),
answers: arrayShuffle([
...element.incorrect_answers.map(
(x) => new AnswerObj(x, false, uniqid())
),
new AnswerObj(element.correct_answer, false, uniqid()),
]),
};
})
)
);
console.log(game);
}, []);
var wholeQ = game.map((element) => {
return (
<Question wQ={element} holdAnswer={() => holdAnswer(element.key)} />
);
});
in Question Component:
export default function Question(props) {
const answers = arrayShuffle(props.wQ.answers).map((element) => {
return <Answers wholeAnswer={element} holdAnswer={props.holdAnswer} />;
});
return (
<div className="question">
<p>{decode(props.wQ.question)}</p>
<div className="answer-buttons-wrapper">{answers}</div>
</div>
);
}
in Answers Component:
export default function Answers(props) {
return (
<button className="answer-button" onClick={props.holdAnswer}>
{decode(props.wholeAnswer.answer)}
</button>
);
}
I believe the problem lies in the following block:
function holdAnswer(qKey) {
console.log(qKey);
setGame((oldGame) => {
oldGame.map((element) => {
return qKey === element.key ? {} : element;
});
});
console.log(qKey);
}
As setGame ends up not returning anything and therefor sets the state with an undefined value.
To address this we can remove the curly-braces in setGame in-order to make it an "implicit return".
Alternatively, we can add a return statement before the mapping function.
// Either this ->
setGame((oldGame) =>
oldGame.map((element) => {
return qKey === element.key ? {} : element;
});
);
// Or this ->
setGame((oldGame) => {
return oldGame.map((element) => {
return qKey === element.key ? {} : element;
});
});
I wanted to pass use-translation as a parameter to util function I created, I'm not able get the desired result, how can I pass a function?
import { useTranslation } from "react-i18next";
import { submissionTypes } from "../../Submissions/Details/constants";
import { getFormName } from "./helpers";
// jest.mock("react-i18next", () => ({
// useTranslation: () => ({ t: key => key }),
// }));
// jest.mock('react-i18next', () => ({
// useTranslation: () => ({
// i18n: { changeLanguage: jest.fn() },
// t: key => key,
// }),
// }));
describe("utils/helpers", () => {
const { t } = useTranslation();
describe("getFormName()", () => {
it("should return withdrawal", () => {
const result = getFormName("withdrawal", true, t ); //last parameter is 't' which is a translation function
expect(result).toEqual("WITHDRAWAL");
});
});
});
Below if function definiton for getFormName:
export function getFormName(type, uppercase, t) {
switch (type) {
case "withdrawal":
return uppercase
? _.upperCase(t("mission.withdrawal"))
: t("mission.withdrawal");
default:
return uppercase ? _.upperCase(type) : toTitleCase(type);
}
}
I am doing a React JS Cart and I am having problems when I try to delete an Item from the there. It has already a function that adds the items and also another for the total quantity and the total price.
This is the ContextProvider:
import { useState } from "react";
import { CartContext } from "./CartContext";
export const CartProvider = ({ children }) => {
const [list, setList] = useState([]);
const addCart = (varietalCount) => {
if (list.find((item) => item.id === varietalCount.id)) {
const newVarietal = list.map((varietal) => {
if (varietal.id === varietalCount.id) {
return { ...varietal, count: varietalCount.count + varietal.count };
}
return varietal;
});
setList(newVarietal);
} else {
setList((state) => {
return [...state, varietalCount];
});
}
};
console.log("list", list);
// const deleteProd = (varietalCount) => {
// if (list.find((item) => item.id === varietalCount.id)) {
// const deleteVarietal = list.map((varietal) => {
// if (varietal.id === varietalCount.id) {
// return { ...varietal, count: null };
// }
// return varietal;
// });
// setList(deleteVarietal);
// } else {
// setList((state) => {
// return [...state, varietalCount];
// });
// }
// };
const totalPrice = () => {
return list.reduce((prev, next) => (prev + (next.count * next.price)), 0)
};
const totalQuantity = () => {
return list.reduce((prev, next) => (prev + (next.count)), 0)
};
return(
<>
<CartContext.Provider value={{ list, addCart, totalPrice, totalQuantity }}>
{children}
</CartContext.Provider>
</>);
};
If it is necessary I can add to the post the Cart.js or the ItemDetail.js. I hope someone can help me. Cheers
I think you can just use filter given that your state has value of an array. Something like:
const deleteProd = (varietalCount) => {
const newItems = list.filter((item) => item.id !== varietalCount.id)
setList(newItems);
};
You can check more array functions from here https://www.w3schools.com/jsref/jsref_obj_array.asp
I have this code for my context provider, I have my wrapped in component but still when I try to use it in a child using either useProductState or useProductDispatch, it returns undefined (throws err);
import React from "react";
import productsReducer from "./productsReducer";
const ProductsStateContext = React.createContext();
const ProductsDispatchContext = React.createContext();
const initialState = {
restaurantTitle: "",
restaurantId: "VljSa5Eakepw9QkTAUOW",
productsCollection: "",
categories: [],
defaultCategory: "",
isLoading: true,
};
function ProductsProvider({ children }) {
const [state, dispatch] = React.useReducer(productsReducer, initialState);
return (
<ProductsStateContext.Provider value={state}>
<ProductsDispatchContext.Provider value={dispatch}>
{children}
</ProductsDispatchContext.Provider>
</ProductsStateContext.Provider>
);
}
function useProductsState() {
const context = React.useContext(ProductsStateContext);
if (context === undefined) {
throw new Error("useProductsState must be used within a ProductsProvider");
}
return context;
}
function useProductsDispatch() {
const context = React.useContext(ProductsDispatchContext);
if (context === undefined) {
throw new Error(
"useProductsDispatch must be used within a ProductsProvider"
);
}
return context;
}
export { ProductsProvider, useProductsState, useProductsDispatch };
Can somebody explain how this works, I'm trying to access state and dispatch into a functional component that is a child of .
UPDATE:
I've got this as an action for my reducer
case "FETCH_RESTAURANT_DATA": {
return fetchRestaurantData(state, action.payload);
}
Function body looks like this:
const fetchRestaurantData = (state, value) => {
let newState = state;
return axios
.post(api.routes.restaurant, { restaurantId: state.restaurantId })
.then((res) => {
newState.restaurantTitle = res.data.restaurantTitle;
res.data.categories.forEach(
(category) =>
(newState.categories[category] = {
loaded: false,
props: [],
})
);
newState.defaultCategory = res.data.categories[0];
newState.productsCollection = res.data.productsCollection;
newState.isLoading = false;
return axios.post(api.routes.category, {
productsCollection: res.data.productsCollection,
categoryId: newState.defaultCategory,
});
})
.then((res) => {
newState.categories[newState.defaultCategory].props =
res.data[newState.defaultCategory];
newState.categories[newState.defaultCategory].loaded = true;
console.log(newState);
return newState;
});
};
What i think is going on, I think in reducer it does not wait for my response and update context state with an undefined value which then triggers my error.
I have tried to make a middle async function that awaits for fetchRestaurantData() response but it is still updating before getting a response
You should wait for the response in fetchRestaurantData:
const fetchRestaurantData = async (state, value) => { // add async keyword to the function
let newState = state;
return await axios // here add await
.post(api.routes.restaurant, { restaurantId: state.restaurantId })
.then((res) => {
newState.restaurantTitle = res.data.restaurantTitle;
res.data.categories.forEach(
(category) =>
(newState.categories[category] = {
loaded: false,
props: [],
})
);
newState.defaultCategory = res.data.categories[0];
newState.productsCollection = res.data.productsCollection;
newState.isLoading = false;
return axios.post(api.routes.category, {
productsCollection: res.data.productsCollection,
categoryId: newState.defaultCategory,
});
})
.then((res) => {
newState.categories[newState.defaultCategory].props =
res.data[newState.defaultCategory];
newState.categories[newState.defaultCategory].loaded = true;
console.log(newState);
return newState;
});
};
More information about the async functions
I am simply looking to save and restore a search term(form data) when a page is refreshed/reloaded. I have tried several solutions to no avail.
Flow: A user submits a search term and is taken to Spotify to retrieve an accessToken, if it is not already available. The initial page is refreshed once the accessToken is retrieved, but the search must be re-entered. This is not good UX.
I concluded that Web Storage was they way to go, of course it is not the only route. I am not sure if this is something that should be relegated to Lifecycle methods: componentDidMount() & componentDidUpdate(). Perhaps that is overkill? In any event, I attempted to employ both localStorage and sessionStorage. My implementation is obviously off as I am not getting the expected result. React dev tools displays the state of the SearchBar term, but it is not being saved. Also of note is the following: React dev tools shows that the onSubmit event handler is registering as bound () {} instead of the expected bound handleInitialSearchTerm() {}. The console also shows that there are no errors.
No third-party libraries please.
SearchBar.js
import React from 'react';
import "./SearchBar.css";
class SearchBar extends React.Component {
constructor(props) {
super(props);
this.state = {
term: this.handleInitialSearchTerm
};
this.search = this.search.bind(this);
this.handleInitialSearchTerm = this.handleInitialSearchTerm.bind(this);
this.setSearchTerm = this.setSearchTerm.bind(this);
this.handleSearchOnEnter = this.handleSearchOnEnter.bind(this);
this.handleTermChange = this.handleTermChange.bind(this);
}
handleInitialSearchTerm = (event) => {
if (typeof (Storage) !== "undefined") {
if (localStorage.term) {
return localStorage.term
} else {
return this.setSearchTerm(String(window.localStorage.getItem("term") || ""));
}
}
};
setSearchTerm = (term) => {
localStorage.setItem("term", term);
this.setState({ term: term });
}
search() {
this.props.onSearch(this.state.term);
}
handleSearchOnEnter(event) {
if (event.keyCode === 13) {
event.preventDefault();
this.search();
}
}
handleTermChange(event) {
this.setState({
term: event.target.value
});
}
render() {
return (
<div className="SearchBar">
<input
placeholder="Enter A Song, Album, or Artist"
onChange={this.handleTermChange}
onKeyDown={this.handleSearchOnEnter}
onSubmit={this.handleInitialSearchTerm}
/>
<button className="SearchButton" onClick={this.search}>
SEARCH
</button>
</div>
);
}
}
export default SearchBar;
Motify.js
let accessToken;
const clientId = "SpotifyCredentialsHere";
const redirectUri = "http://localhost:3000/";
const CORS = "https://cors-anywhere.herokuapp.com/"; // Bypasses CORS restriction
const Motify = {
getAccessToken() {
if (accessToken) {
return accessToken;
}
// if accessToken does not exist check for a match
const windowURL = window.location.href;
const accessTokenMatch = windowURL.match(/access_token=([^&]*)/);
const expiresInMatch = windowURL.match(/expires_in=([^&]*)/);
if (accessTokenMatch && expiresInMatch) {
accessToken = accessTokenMatch[1]; //[0] returns the param and token
const expiresIn = Number(expiresInMatch[1]);
window.setTimeout(() => accessToken = "", expiresIn * 1000);
// This clears the parameters, allowing us to grab a new access token when it expires.
window.history.pushState("Access Token", null, "/");
return accessToken;
} else {
const accessUrl = `https://accounts.spotify.com/authorize?client_id=${clientId}&response_type=token&scope=playlist-modify-public&redirect_uri=${redirectUri}`;
window.location = accessUrl;
}
},
search(term) {
const accessToken = Motify.getAccessToken();
const url = `${CORS}https://api.spotify.com/v1/search?type=track&q=${term}`;
return fetch(url, { headers: { Authorization: `Bearer ${accessToken}` }
}).then(response => response.json()
).then(jsonResponse => {
if (!jsonResponse.tracks) {
return [];
}
return jsonResponse.tracks.items.map(track => ({
id: track.id,
name: track.name,
artist: track.artists[0].name,
album: track.album.name,
uri: track.uri,
preview_url: track.preview_url
}));
})
}
...
Please check the code I have added.
Changes I did are below:
1)
this.state = {
term: JSON.parse(localStorage.getItem('term')) || '';
};
setSearchTerm = (term) => {
this.setState({
term: term
},
() => {
localStorage.setItem('term', JSON.stringify(this.state.term)));
}
import React from 'react';
import "./SearchBar.css";
class SearchBar extends React.Component {
constructor(props) {
super(props);
this.state = {
term: JSON.parse(localStorage.getItem('term')) || '';
};
this.search = this.search.bind(this);
this.handleInitialSearchTerm = this.handleInitialSearchTerm.bind(this);
this.setSearchTerm = this.setSearchTerm.bind(this);
this.handleSearchOnEnter = this.handleSearchOnEnter.bind(this);
this.handleTermChange = this.handleTermChange.bind(this);
}
handleInitialSearchTerm = (event) => {
if (typeof(Storage) !== "undefined") {
if (localStorage.term) {
return localStorage.term
} else {
return this.setSearchTerm(String(window.localStorage.getItem("term") || ""));
}
}
};
setSearchTerm = (term) => {
this.setState({
term: term
},
() => {
localStorage.setItem('term', JSON.stringify(this.state.term)));
}
search() {
this.props.onSearch(this.state.term);
}
handleSearchOnEnter(event) {
if (event.keyCode === 13) {
event.preventDefault();
this.search();
}
}
handleTermChange(event) {
this.setState({
term: event.target.value
});
}
render() {
return ( <
div className = "SearchBar" >
<
input placeholder = "Enter A Song, Album, or Artist"
onChange = {
this.handleTermChange
}
onKeyDown = {
this.handleSearchOnEnter
}
onSubmit = {
this.handleInitialSearchTerm
}
/> <
button className = "SearchButton"
onClick = {
this.search
} >
SEARCH <
/button> <
/div>
);
}
}
export default SearchBar;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.1/umd/react-dom.production.min.js"></script>
If it is in hooks i would have done like below:
import React, {
useEffect,
useState,
useRef,
} from 'react';
function App() {
const [value, setValue] = useState(() => {
if (localStorage.getItem('prevCount') === null) {
return 0;
} else {
return localStorage.getItem('prevCount');
}
});
const countRef = useRef();
useEffect(() => {
countRef.current = value;
if (countRef.current) {
localStorage.setItem('prevCount', countRef.current);
} else {
localStorage.setItem('prevCount', 0);
}
});
const handleIncrement = () => {
setValue((value) => +value + 1);
};
const handleDecrement = () => {
if (value === 0) {
return;
} else {
setValue((value) => value - 1);
}
};
return (
<div className="card">
<label className="counterLabel">Simple Counter</label>
<button
className="button"
onClick={handleIncrement}
>
Increment
</button>
<span className="count">{value}</span>
<button
className="button"
onClick={handleDecrement}
>
Decrement
</button>
</div>
);
}
export default App;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.1/umd/react-dom.production.min.js"></script>
So what the above code is doing is that when we inititalize the state value we first check the localStorage , if "term" has value in localStorage we will use that value or else an empty string is initialized.
Using callback of setState inside the method setSearchTerm we set the term value immediately
Try the useLocalStorage hook to save search client side.
// useLocalStorage Hook to persist values client side
function useLocalStorage(key, initialValue) {
// State to store our value
// Pass initial state function to useState so logic is only executed once
const [storedValue, setStoredValue] = useState(() => {
if (typeof window === "undefined") {
return initialValue;
}
try {
// Get from local storage by key
const item = window.localStorage.getItem(key);
// Parse stored json or if none return initialValue
return item ? JSON.parse(item) : initialValue;
} catch (error) {
// If error also return initialValue
console.log(error);
return initialValue;
}
});
// Return a wrapped version of useState's setter function that ...
// ... persists the new value to localStorage.
const setValue = (value) => {
try {
// Allow value to be a function so we have same API as useState
const valueToStore =
value instanceof Function ? value(storedValue) : value;
// Save state
setStoredValue(valueToStore);
// Save to local storage
if (typeof window !== "undefined") {
window.localStorage.setItem(key, JSON.stringify(valueToStore));
}
} catch (error) {
// A more advanced implementation would handle the error case
console.log(error);
}
};
return [storedValue, setValue];
}
credit: Brandon Baars