import { createContext } from 'react';
import axios from 'axios'
import keys from '../../keys'
const getCollection = () => {
console.log("getCollectionCalled")
return axios.get(`${keys.url}/collection`)
.then(data => {
console.log(data)
return data
})
.catch(err => {
return {}
})
}
const SHOP_DATA = getCollection();
const CollectionsContext = createContext(SHOP_DATA);
export default CollectionsContext;
This is the code I am trying to run but context is not setting as per the data from the server, when I am using this in other component like this
const collections = useContext(CollectionsContext);
console.log("COLLECTIONS ", collections)
It is consoling it as :
COLLECTIONS PromiseĀ {<pending>}__proto__:
Promise[[PromiseStatus]]: "resolved"
[[PromiseValue]]: undefined
Kindly rectify me I am unable to think how may I implement it.
In this case you're returning a promise as initial context value, what you need to do is something between these lines:
import React, { useState, useEffect, createContext, useContext } from 'react'
import axios from 'axios'
import keys from '../../keys'
const INITIAL_STATE = {}
const CollectionsContext = createContext(INITIAL_STATE)
const Provider = ({ children }) => {
const [collection, setCollection] = useState()
useEffect(() => {
axios.get(`${keys.url}/collection`)
.then(data => setCollection(data))
.catch(() => setCollection(INITIAL_STATE))
}, [])
return (
<CollectionsContext.Provider value={collection}>
{children}
</CollectionsContext.Provider>
)
}
export const useCollection = () => useContext(CollectionsContext)
export default Provider
Then you wrap the top level (as high as it's needed, not neccessarily the highest) with the provider:
import CollectionsProvider from '.../.../somewhere'
<CollectionsProvider>...rest of components...</CollectionsProvider>
In this case, INITIAL_STATE is the value provided until value is undefined or you haven't used Provider. For example, you use useCollection outside of CollectionsProvider.
Related
Functions such as add and remove from cart works fine. But the data is not saved successfully after the browser is refreshed. How to keep the data in the cart even when the browser is refreshed.
here is my code. if there is any code you would like to see let me know
import React, { useEffect } from "react";
import { useStateValue } from "../hooks/StateProvider";
function CheckoutPage() {
const [{ basket }, dispatch] = useStateValue();
useEffect(() => {
window.localStorage.setItem("basket", JSON.stringify(basket));
}, [basket]);
useEffect(() => {
const basketData = window.localStorage.getItem("basket");
if (basketData !== null) dispatch(JSON.parse(basketData));
}, []);
import React, { createContext, useContext, useReducer } from "react";
export const StateContext = createContext();
export const StateProvider = ({ reducer, initialState, children }) => (
<StateContext.Provider value={useReducer(reducer, initialState)}>
{children}
</StateContext.Provider>
);
export const useStateValue = () => useContext(StateContext);
You have to use Web Api for that . sessionStorage , localStorage , Indexed DB or etc..
rootReducer
import { combineReducers } from "redux";
import mods from "./mods.js";
export default combineReducers({
mods
})
reducers/mods.js
import { GET_MODS, GET_SPECIFC_MOD } from "../actions/types"
const initialState = {
mods: [],
currMod: []
}
export default function(state = initialState, action) {
switch(action.type) {
case GET_MODS:
return {
...state,
mods: action.payload
}
case GET_SPECIFC_MOD:
return {
...state,
currMod: action.payload
}
default:
return state
}
}
actions/mods.js
import axios from 'axios'
import { GET_MODS, GET_SPECIFC_MOD } from './types'
// get the mods
export const getMods = () => dispatch => {
axios.get('http://localhost:8000/api/mods')
.then(res => {
dispatch({
type: GET_MODS,
payload: res.data
})
}).catch(err => console.log(err))
}
// get single mod
export const getSpecificMod = (title) => dispatch => {
axios.get(`http://localhost:8000/api/mods/${title}`)
.then(res => {
dispatch({
type: GET_SPECIFC_MOD,
payload: res.data
})
}).catch(err => console.log(err))
}
components/download.js
import React from 'react'
import { useState, useEffect } from 'react'
import { connect } from 'react-redux'
import { getSpecificMod } from '../actions/mods'
const Download = () => {
useEffect(() => {
const title = window.location.pathname.split('/')[3]
getSpecificMod(title)
})
return (
<></>
)
}
const mapStateToProp = state => ({
currMod: state.mods.currMod
})
export default connect(mapStateToProp, getSpecificMod)(Download)
Response from backend
GET http://localhost:8000/api/mods/function(){return!window.__REDUX_DEVTOOLS_EXTENSION_LOCKED__&&a.dispatch.apply(a,arguments)}
Basically the user clicks on a mod and gets sent to the download section that is handled by 'download.js' the component ('download.js') renders it and reads the window.location to retrieve the title, with redux I want to get the mod so i made a function that takes the title and sends the request 'getMod(title)' but for some reason it is throwing horrible errors that I dont understand, any help is appreciated!
You are not dispatching the action properly in your component. Right now you are actually just calling the getSpecificMod action creator function from your imports. Your Download component doesn't read anything from props so it is ignoring everything that gets created by the connect HOC.
If you want to keep using connect, you can fix it like this:
import React, { useEffect } from 'react'
import { connect } from 'react-redux'
import { getSpecificMod } from '../actions/mods'
const Download = ({currMod, getSpecificMod}) => {
const title = window.location.pathname.split('/')[3]
useEffect(() => {
getSpecificMod(title)
}, [title])
return (
<></>
)
}
const mapStateToProps = state => ({
currMod: state.mods.currMod
})
export default connect(mapStateToProps, {getSpecificMod})(Download)
We are now accessing the bound action creator as a prop of the component. mapDispatchToProps is an object which maps the property key to the action.
But it's better to use the useDispatch hook:
import React, { useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { getSpecificMod } from '../actions/mods'
const Download = () => {
const currentMod = useSelector(state => state.mods.currMod);
const dispatch = useDispatch();
const title = window.location.pathname.split('/')[3]
useEffect(() => {
dispatch(getSpecificMod(title));
}, [title, dispatch]);
return (
<></>
)
}
export default Download;
There might be some confusion on terminology here. Your getSpecificMod function is a function which takes dispatch as an argument but it is not a mapDispatchToProps. It is a thunk action creator.
Make sure that you have redux-thunk middleware installed in order to handle this type of action. Or better yet, use redux-toolkit.
Your useEffect hook needs some sort of dependency so that it knows when to run. If you only want it to run once you can use an empty array [] as your dependencies. If you don't specify the dependencies at all then it will re-run on every render.
Does the pathname change? If so, how do you know when? You might want to add an event listener on the window object. Or consider using something like react-router. But that is a separate question.
I currently have a component which builds its payload from the context stored in a redux store.
When completing the API call it then dispatches nextTask which updates the state of the context causing the effect to run again and be stuck in an infinite loop.
I need to be able to access the context when building the payload but only re-fire the fetch event when the url changes. I tried useCallback but may have misunderstood.
Here is the component:
import React, { useCallback, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import Axios from "../../config/AxiosConfig";
import { nextTask } from "../../redux/actions/flowActions";
import { RootState } from "../../redux/reducers/rootReducer";
import { JsonRPC } from "../../types/tasks";
const JsonRpc: React.FC<JsonRPC> = ({
url,
method,
payload,
payload_paths,
...rest
}) => {
const dispatch = useDispatch();
const context = useSelector((state: RootState) => state.context.present);
const getPayload = useCallback(() => {
console.log(context);
}, [context]);
useEffect(() => {
const fetch = async () => {
const payload = getPayload();
const { data } = await Axios({
method,
url,
// ...opts,
});
if (data.result) {
dispatch(nextTask());
}
};
try {
fetch();
} catch (e) {
console.log(e);
}
}, [url, dispatch, method, getPayload]);
return null;
};
export default JsonRpc;
I have a react-native, redux app, and after upgrading I've started getting some warnings about lifecycle hooks. My code looks like below:
import React from 'react';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { selectPosts} from '../redux/selectors/postSelector';
import { getPosts } from '../redux/actions/postActions';
class BasicScreen extends React.Component {
state = {
data: [],
myItems: [],
};
componentWillMount() {
this.getPosts();
}
componentDidMount() {
this.checkforItems();
}
getPosts = async () => {
// Call to a redux action
await this.props.getPosts();
};
checkforItems = async () => {
// myItems in initial state are set from data in
AsyncStorage.getItem('MyItems').then(item => {
if (item) {
this.setState({
myItems: JSON.parse(item),
});
} else {
console.log('No data.');
}
});
};
componentWillReceiveProps(nextProps) {
// Data comes from the redux action.
if (
nextProps.data &&
!this.state.data.length &&
nextProps.data.length !== 0
) {
this.setState({
data: nextProps.data,
});
}
}
render() {
return (
<View>/* A detailed view */</View>
)
}
}
const mapStateToProps = createStructuredSelector({
data: selectPosts,
});
const mapDispatchToProps = dispatch => ({
dispatch,
getPosts: () => dispatch(getPosts()),
});
export default connect(mapStateToProps, mapDispatchToProps)(BasicScreen);
To summarize, I was calling a redux action (this.getPosts()) from componentWillMount(), and then updating the state by props received in componentWillReceiveProps. Now both these are deprecated, and I am getting warnings that these are deprecated.
Apart from this, I am also setting some initial state by pulling some data from storage (this.checkforItems()). This gives me another warning - Cannot update a component from inside the function body of a different component.
To me it looks like the solution lies in converting this into a functional component, however, I'm stuck at how I will call my initial redux action to set the initial state.
UPDATE:
I converted this into a functional component, and the code looks as follows:
import React, { Fragment, useState, useEffect } from 'react';
import { connect } from 'react-redux';
import AsyncStorage from '#react-native-community/async-storage';
import { StyleSheet,
ScrollView,
View,
} from 'react-native';
import {
Text,
Right,
} from 'native-base';
import { createStructuredSelector } from 'reselect';
import {
makeSelectPosts,
} from '../redux/selectors/postSelector';
import { getPosts } from '../redux/actions/postActions';
const BasicScreen = ({ data, getPosts }) => {
const [myData, setData] = useState([]);
const [myItems, setItems] = useState([]);
const checkForItems = () => {
var storageItems = AsyncStorage.getItem("MyItems").then((item) => {
if (item) {
return JSON.parse(item);
}
});
setItems(storageItems);
};
useEffect(() => {
async function getItems() {
await getPosts(); // Redux action to get posts
await checkForItems(); // calling function to get data from storage
setData(data);
}
getItems();
}, [data]);
return (
<View>
<>
<Text>{JSON.stringify(myItems)}</Text>
<Text>{JSON.stringify(myData)}</Text>
</>
</View>
);
}
const mapStateToProps = createStructuredSelector({
data: makeSelectPosts,
});
const mapDispatchToProps = dispatch => ({
dispatch,
getPosts: () => dispatch(getPosts()),
});
export default connect(mapStateToProps, mapDispatchToProps)(BasicScreen);
It works, but the problem is that the first Text - {JSON.stringify(myItems)} - it is rerendering continuously. This data is actually got using checkForItems(). I wanted the useEffect to be called again only when the data updates, but instead something else is happening.
Also, I noticed that setData is not being called correctly. The data becomes available through the prop (data), but not from the state (myData). myData just returns empty array.
I have a list of object stored as a array in my redux store which loads on component mount. I want to List them in a div, also to do the crud Operation. This is my implementation. Whenever I use useSelector to save the list for a constants it fectching infinite number of logs.
BranchAction.js
import axios from 'axios';
export const fetchAllBranchListOk = (branchList) => {
return {
type : 'FETCH_ALL_BRANCH_LIST_OK',
branchList
}
};
export const fetchAllBranchList = () =>{
return (dispatch) => {
return axios.get(`https://jsonplaceholder.typicode.com/posts`)
.then(response => {
dispatch(fetchAllBranchListOk(response.data));
})
.catch(error => {
throw(error);
});
}
};
BranchReducer
export default (state = [], action) => {
switch (action.type) {
case 'FETCH_ALL_BRANCH_LIST_OK' :
return action.branchList;
default:
return state;
}
};
BranchManagement.js
function BranchManagement() {
store.dispatch(BranchAction.fetchAllBranchList());
const AllBranch = useSelector(state => state.BranchReducer)
return(
<div>
</div>
)
}
export default BranchManagement;
CombinedReducer -> index.js
import {combineReducers} from 'redux'
import BranchReducer from "./Admin/BranchReducer";
const Reducers = combineReducers({
BranchReducer
});
export default Reducers;
If you want to dispatch the action to fetch the data from the backed, you should be keeping those calls in useEffect hook. The purpose of useEffect is similar to the purpose of Lifecycle methods in the class component like componentDidMount, componentDidUpdate and componentWillUnMount. To understand more about useEffect please refer this.
import React from "react";
import { useSelector, useDispatch } from "react-redux";
import BranchAction from "/path/to/BranchAction";
function BranchManagement() {
const dispatch = useDispatch();
//Since the data in the state is on `branchList`. You can directly return
//`state.branchList` which will you provide you the data you are looking for.
const branchList = useSelector(state => state.branchList)
//It'll act similar to `componentDidMount`. Since we are passing `[]`
//to `useEffect` dependencies array
useEffect(() => {
dispatch(BranchAction.fetchAllBranchList());
}, [])
//Here I'm assuming `branchList` is array of objects with `name` and `id`.
//Updated answer with branchList as[{"branchID":1,"createdBy":1,"isActive":true,"branchDetails":{"branchDetailsID":1}},{"branchID":2,"createdBy":1,"isActive":true,"branchDetails":{"branchDetailsID":1}}]
return(
<div>
{
(branchList || []).map((branch, index) => {
<div key={branch.branchID || index}>
<span>{branch.branchID}</span>
<span>{branch.createdBy}</span>
<span>{branch.isActive}</span>
<span>{branch.branchDetails.branchDetailsID}</span>
</div>
}
}
</div>
)
}
export default BranchManagement;
Hope this helps in order to resolve the issue.