I have a need to fetch data from an API on component load, am using axios to fetch data, I need to save the response to the state and get back when the component load.
But i could do as am new to this.
My codes as below.
Sales.js : (This is where I fetch My components)
function SalesDesk() {
return (
<div>
<FoodScreen />
</div>
)}
export default SalesDesk;
FoodScreen.js (This is where i need to list my results to a variable, to map it later)
function FoodScreen() {
return(
<div className="sdFoodScreenMain">
{console.log(items)} // The results should be displayed here
</div>
)}
export default FoodScreen;
API.js (Here is where where i use my axios Router)
const API_URL = `https://jsonplaceholder.typicode.com/`; //Mock api for test purposes
export const GetAllItems = () => {
return (dispatch) => {
axios.get(API_URL)
.then(response => {
dispatch(allItemsList(response.data));
})
}
};
ItemsReducer.js (The reducer Logic)
const ItemsReducer =(state:Array = null, action) =>{
if (action.type === 'ALL_ITEMS') {
return GetAllItems ;
} else {
return state= null;
}
};
export default ItemsReducer
SalesAction.js (Action list)
export const allItemsList = () => {
return {
type: 'ALL_ITEMS'
};
};
All I need to do is fetch the the data from the API and display it in the console, when the component renders.so that I can display it in a map of div boxes for future purposes. Am new to both react and Redux, so ignore if any logic or implementation issues.
At first Router.js is a bad name(api.js etc), You should connect Sales.js to redux, using { connect } from 'react-redux'. See there https://redux.js.org/basics/usage-with-react and call action to fetch data in Sales.js
All I had to add an useDispatch on the component render, so it could fetch the data to the component on load.
import Reactfrom 'react'
import {useDispatch} from "react-redux";
import {GetAllItems} from 'API' //File containing the axios function
export function SalesDesk() {
const dispatch = useDispatch();
dispatch(GetAllItems())
return (
<div>
<FoodScreen />
</div>
)}
This helped me to fetch add add to state on component load.
Related
In next.js We can request to api in getServerSideProps and pass data to page through props like this :
method 1: (next default approach)
const Page: NextPage = ({page}) => {
return <div>{JSON.stringify(data)}</div>;
};
// This gets called on every request
export async function getServerSideProps() {
// Fetch data from external API
const res = await fetch(`https://.../data`)
const data = await res.json()
// Pass data to the page via props
return { props: { data } }
}
export default Page
I work in large scale project So I want to manage all server and client request only using thunk and then store it to redux and use in page like this:
method 2: (with next-redux-wrapper)
import React from 'react';
import {NextPage} from 'next';
import {useSelector} from 'react-redux';
import {wrapper, State, fetchData } from '../store';
const Page: NextPage = () => {
const {data} = useSelector(state => state);
return <div>{JSON.stringify(data)}</div>;
};
export const getServerSideProps = wrapper.getServerSideProps(store => ({req, res, ...etc}) => {
store.dispatch(fetchData());
});
export default Page;
I want to know which is better approach ?
Is it anti-pattern to use second approach to store all pre-rendered data and client side state of app in redux?
I am using redux with redux-thunk middleware. The function in question makes a GET request to an API and upon response (.then()) dispatches the res to my redux store via an action.
For some reason when I pass dispatch to the parent function the function never runs. When I remove dispatch the parent function does run...(???) I have multiple other components within the same app that follow this exact same pattern successfully. For some reason this particular component is behaving in this strange way although i've triple checked and the boilerplate is all the same.
Here is my store.jsx:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import logger from 'redux-logger';
import rootReducer from '../reducers/root_reducer'
const configureStore = (preloadedState = {}) =>
createStore(
rootReducer,
preloadedState,
applyMiddleware(thunk, logger)
);
export default configureStore;
my actions my_team_actions.js:
import * as APIUtil from '../util/api/my_team_api_util';
export const RECEIVE_ORG_SURVEY = "RECEIVE_ORG_SURVEY"
export const receiveOrgSurvey = survey => ({
type: RECEIVE_ORG_SURVEY,
survey
});
export const getOrganizationSurvey = () => dispatch => {
debugger
APIUtil.getOrgSurvey()
.then((res) => {
debugger
dispatch(receiveOrgSurvey(res))
})
.catch(err => console.log(err))
}
my API call my_team_api_util.js:
import axios from 'axios';
export const getOrgSurvey = () => {
return axios.get(`/api/mongo/organizations/test`)
}
component container my_team_container.jsx:
import { connect } from 'react-redux';
import MyTeam from './my_team';
import { getOrganizationSurvey } from '../../actions/my_team_actions';
const mSTP = state => {
return {
user: state.session.user,
};
};
const mDTP = dispatch => {
return {
getSurvey: () => getOrganizationSurvey(),
};
};
export default connect(mSTP, mDTP)(MyTeam);
component my_team.jsx:
import React from 'react';
class MyTeam extends React.Component {
constructor(props) {
super(props)
this.createTeam = this.createTeam.bind(this);
}
createTeam() {
this.props.getSurvey();
}
render() {
return (
<div className="my-team-frame frame">
<div className="my-team-container">
<div className="contact-data-container">
<div className="contact-data-header header">Contact a Data Scientist</div>
</div>
<div className="myteam" onClick={this.createTeam}>BUTTON</div>
</div>
</div>
)
}
}
export default MyTeam;
On the client side the my_team component renders fine and when I click the button which calls the function which will eventually dispatch my action it only seems to run when dispatch is NOT included in getOrganizationSurvey() in my_team_actions.js i.e. I hit both debuggers (and the second one with a correct res object). When dispatch is included (as shown in the snippet above) I don't hit either debuggers nor are any errors thrown.
I'm really scratching my head on this, any input is appreciated!
Thanks,
Ara
God I am a moron... XD
I said I triple checked... I should have checked 4 times! The issue was in my components container my_team_container.jsx I simply forgot to pass dispatch in the map dispatch to props object!
I fixed it by adding dispatch to the getSurvey callback...
my_team_container.jsx
import { connect } from 'react-redux';
import MyTeam from './my_team';
import { getOrganizationSurvey } from '../../actions/my_team_actions';
const mSTP = state => {
return {
user: state.session.user,
};
};
const mDTP = dispatch => {
return {
getSurvey: () => dispatch(getOrganizationSurvey()),
};
};
export default connect(mSTP, mDTP)(MyTeam);
it's funny how you can spend 2 hours on a problem, think it's hopeless and then as soon as you ask for help take another look at it and the solution just stares right back at you 😂
I am creating an ecommerce app with Nextjs and want to share data between pages. I know that we can't use props to pass data between the pages and so was looking into react context api. This is my first time using react context api. I've researched and found that you should add the Provider in the _app.js page in nextjs.
But this shares the data among all the pages. Plus my data is being retrieved by getStaticProps in the slug page of the app. I want to get this data into the checkout page of my app.
This is the context I have created:
import { createContext, useState, useContext } from 'react';
const productContext = createContext({} as any);
export const ProductProvider = ({ children }) => {
const [productData, setProductData] = useState('');
return <productontext.Provider value={{ productData, setProductData }}>{children}</productContext.Provider>;
};
export const useProduct = () => useContext(productContext);
_app.js
import { ReportProvider } from '../contexts/ReportContext';
export default function CustomApp({ Component, pageProps }) {
return (
<ReportProvider>
<Component {...pageProps} />
</ReportProvider>
);
}
I import this into the slug page and try to update the state from here
// [slug].js
import client from '../../client'
import {useProduct} from './productContext';
const Post = (props) => {
const {setProductData} = useProduct();
const { title = 'Missing title', name = 'Missing name' , price} = props
setProductData(title);
return (
<article>
<h1>{title}</h1>
<span>By {name}</span>
<button>
Buy Now
</button>
</article>
)
}
Post.getInitialProps = async function(context) {
const { slug = "" } = context.query
return await client.fetch(`
*[_type == "post" && slug.current == $slug][0]{title, "name": author->name, price}
`, { slug })
}
export default Post
However this productData is not accessible from another page and the react context state is not getting updated.
Any idea why this could be happening?
Once you've updated your context value. Please make sure you are using next/link to navigate between pages. Here is details about next/link
So, i have component called Balance component which i want to display in navbar and on a page (summary page) component as well.
But i want to prevent it from making an api request twice.
The code of balanceSlice:
import {createSlice, createAsyncThunk} from '#reduxjs/toolkit';
export const updateBalance = createAsyncThunk(
'balance/fetchBalance',
async () => {
const response = await fetchBalance();
return response.data;
}
);
const initialState = {
value: 0
};
export const balanceSlice = createSlice({
name: 'balance',
initialState,
reducers: {
},
extraReducers: () => {...}
});
export const selectBalance = (state) => state.balance.value;
export default balanceSlice.reducer;
Here is the code of Balance
import {useState, useEffect} from 'react';
import {useSelector, useDispatch} from 'react-redux';
import {updateBalance, selectBalance} from './balanceSlice';
function Balance() {
const balance = useSelector(selectBalance);
const dispatch = useDispatch();
useEffect(() => {
dispatch(updateBalance());
console.log('component ran')
}, [dispatch]);
return (
<>
{balance}
</>
);
}
export default Balance;
As i want to reuse this component at multiple places, so i haven't added any styling in this component. The styling is being done in summary page and the navbar. The updateBalance is requesting new balance from the api. I am using redux toolkit and I am trying to follow single responsibility principle and making component small, reusable and testable.
Is it recommended to render balance component at multiple places?
Navbar code:
import Balance from './../../features/balance/Balance';
function TopNavbar() {
return (
<Navbar>
<span className={styles.navbar__text__span}><Balance /></span>
</Navbar>
);
}
Similarly, i am importing Balance component in summaryPage component and rendering it. Is it a recommended approach or should i simply import selectBalance from balanceSlice and display it in navbar?
You can render Balance components wherever you need.
Balance component would look like this.
function Balance() {
const balance = useSelector(selectBalance);
const dispatch = useDispatch();
useEffect(() => {
if (balance.status === 'ready') {
dispatch(updateBalance());
}
console.log('component ran')
}, [dispatch]);
return (
<>
{balance.value ?? 0}
</>
);
}
In updateBalance action, you should set the balance status to 'fetching' and call the api. After api call is finished, you can set the balance status to 'fetched' and set the balance value to the fetched value. That's all I guess.
I'm writing a react-redux code I defined an action to called in the componant, it's called addCart.
import axios from "axios"
import {GET_PLATS} from "./actionType"
export const getplats = () => dispatch => {
// 2-1 axions get the same path of back in app.use
axios.get("/plat-list").then(res => {
dispatch({
//2-2 same name in action type (after this go to make reducers)
type:GET_PLATS,
payload:res.data
})
})
}
export const addCart =( ) =>{
return (dispatch) => {
console.log("added To cart");
dispatch({
type:GET_PLATS,
})
}
}
then I've wrote this reducer:
import { GET_PLATS } from "../action/actionType"
//first create first main state
const initialState={
plats:[],
cmdElements:[]
}
export default function(state=initialState,action){
switch(action.type){
case GET_PLATS:
return{
...state,
plats:action.payload,
cmdElements:state.cmdElements
}
default :
return state
}
}
Then I called this action in a component**
import React, { Component } from 'react'
import { connect } from 'react-redux'
import {getplats,addCart } from '../../action/action'
import PropTypes from 'prop-types'
const { Meta } = Card
class PlatListeU extends Component {
componentDidMount(){
this.props.getplats()
}
render() {
return (
<div>
<Button type="primary" onClick={ () =>{
this.props.platListe.addCart()
} } >addTocart</Button>
</div>
)}
PlatListeU.propTypes = {
addCart:PropTypes.func.isRequired,
PlatListe:PropTypes.object.isRequired
}
const mapStateToProps =(state) =>{
return{
platListe:state.plats ,
cmdElements:state.cmdElements
}
}
export default connect(mapStateToProps,{ getplats,addCart}) (PlatListeU)
But when I press on the button, I have this error message:
TypeError: this.props.platListe.addCart is not a function
Everything should be fine. I tried a lot of ways but the result is the same . Can anyone help me?
It's just this.props.addCart(). You import the methods in the component, not in platListe. Thus the error.
For the other error:
it looks like your reducers are mapped correctly. so state.moviesReducer is always defined.
if your initial list of items doesn't even load correctly, means your items object are causing the error.
if your initial list loads correctly, and the error occurs only after you dispatch the action, means your update() is mutating your state shape.
I don't know where you are getting this update function. but i am guessing you should just return{items:update(..)}
This is how I would go about debugging your code.
I think it is just this.props.addCart()
I believe you just want this.props.addCart(). The key platListe exists in your state object, it does not have any methods attached to it. You import the standalone methods at the top of your component.