Creating table with data from API using Redux - javascript

I'm attempting to get information about NBA teams using Redux, then put that data into a table. Right now I'm able to get the data from the API, but I'm struggling to figure out the correct syntax to display a table with the data I collect using Redux (I usually use getContext and am trying to familiarize myself with Redux more).
This is my App.js
import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import { getTeams, getTable } from "./features/teams/teamsSlice";
import { Button } from "#mui/material";
import rsLogo from "./logo-with-name.png";
import "./App.css";
function App() {
const dispatch = useDispatch();
const { teams } = useSelector((state) => state.teams);
const { table } = useSelector((state) => state.table);
useEffect(() => {
dispatch(getTeams());
}, [dispatch]);
console.log(teams);
console.log(table);
return (
<div className="App">
<header className="App-header">
<img src={rsLogo} className="App-logo" alt="logo" />
</header>
<main>
<Button
variant="contained"
target="_blank"
onClick={() => dispatch(getTable(teams))}
size="large"
sx={{ m: 2, bgcolor: "#00003C" }}
disableElevation
>
Show Teams
</Button>
{table}
</main>
</div>
);
}
export default App;
This is my store.js
import { configureStore } from "#reduxjs/toolkit";
import teamsReducer from "../features/teams/teamsSlice";
const store = configureStore({
reducer: {
teams: teamsReducer,
},
});
export default store;
And this is teamsSlice.js
import { createSlice, createAsyncThunk } from "#reduxjs/toolkit";
export const getTeams = createAsyncThunk(
"teams/getTeams",
async (dispatch, getState) => {
return await fetch("https://www.balldontlie.io/api/v1/players").then(
(res) => {
return res.json();
}
);
}
);
const teamsSlice = createSlice({
name: "team",
initialState: {
teams: [],
table: "",
status: null,
},
reducers: {
getTable(teams, action) {
console.log("teams is ", teams);
console.log("action is ", action);
return <div>test</div>;
},
},
extraReducers: {
[getTeams.pending]: (state, action) => {
state.status = "loading";
},
[getTeams.fulfilled]: (state, action) => {
state.status = "success";
state.teams = action.payload;
},
[getTeams.rejected]: (state, action) => {
state.status = "failed";
},
},
});
const { actions, reducer } = teamsSlice;
export const { getTable } = actions;
export default teamsSlice.reducer;
I haven't yet created the actual table, I'm just trying to get the syntax correct for being able to click a button and return a table based on data from the store.
Could anyone tell me what I'm doing wrong?

Since you are using redux and your request is not in the file it is important to keep an eye on changes to the object you need the information for, this is one way to do it:
import React, { useState, useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import { getTeams, getTable } from "./features/teams/teamsSlice";
import { Button } from "#mui/material";
import rsLogo from "./logo-with-name.png";
import "./App.css";
function App() {
const dispatch = useDispatch();
const { teams } = useSelector((state) => state.teams);
const { table } = useSelector((state) => state.table);
const [dataTable, setdataTable] = useState([]);
useEffect(() => {
dispatch(getTeams());
}, [dispatch]);
useEffect(() => {
if(table) setdataTable(table);
}, [table])
return (
<div className="App">
<header className="App-header">
<img src={rsLogo} className="App-logo" alt="logo" />
</header>
<main>
<Button
variant="contained"
target="_blank"
onClick={() => dispatch(getTable(teams))}
size="large"
sx={{ m: 2, bgcolor: "#00003C" }}
disableElevation
>
Show Teams
</Button>
{dataTable}
</main>
</div>
);
}
export default App;
with this, when the effect detects a change in that object it will put it in the state when it exists

Related

How to use redux state data in a link?

I am new to redux and reactjs. I am trying to use a state data called type in the link I am fetching using axios in line no 17.
I am setting the value of type from another .jsx file using dispatch().
Here in Home.jsx file I am calling dispatch in line no 24 for updating the state value type with onClick event.
Home.jsx
import React from 'react';
import '../styles/home.scss';
import { Link } from 'react-router-dom';
import { setType } from '../redux/imageSlice';
import { useDispatch } from 'react-redux';
const Home = () => {
const dispatch = useDispatch();
return (
<div className="container">
<div className="row">
<div className="col-sm-6 col-md-4 d-flex justify-content-center my-3">
<div className="ci">
<img
className="img-fluid"
src="https://res.cloudinary.com/djz3p8sux/image/upload/v1662295247/web-projects-images/natures_hodrkk.jpg"
alt="Nature"
/>
<Link to="/nature">
<div className="middle">
<div
className="text"
onClick={() => dispatch(setType('nature'))}>
Nature
</div>
</div>
</Link>
</div>
</div>
</div>
</div>
);
};
export default Home;
imageSlice.js
import { createAsyncThunk, createSlice } from '#reduxjs/toolkit';
import axios from 'axios';
const initialState = {
type: '',
images: [],
error: null,
isLoading: false,
};
const config = {
Authorization: '563492ad6f91700001000001350d302e175b4c208aac413953d6edcc',
};
export const fetchImages = createAsyncThunk('images/fetchImages', async () => {
const res = await axios.get(
`https://api.pexels.com/v1/search?query=${initialState.type}&per_page=15`,
{
headers: config,
}
);
return res.data;
});
export const imageSlice = createSlice({
name: 'images',
initialState,
reducers: {
setType: (state, action) => {
state.type = action.payload;
},
},
extraReducers: builder => {
builder.addCase(fetchImages.pending, state => {
state.isLoading = true;
});
builder.addCase(fetchImages.fulfilled, (state, action) => {
state.isLoading = false;
state.images = action.payload;
state.error = null;
});
builder.addCase(fetchImages.rejected, (state, action) => {
state.isLoading = false;
state.images = [];
state.error = action.error.message;
});
},
});
export const { setType } = imageSlice.actions;
export default imageSlice.reducer;
store.js
import { configureStore } from '#reduxjs/toolkit';
import imageReducer from './imageSlice';
export const store = configureStore({
reducer: {
images: imageReducer,
},
});
How to do that? I am trying to update the fetching link using state type value on line 17 in imageSlice.js file.
One option is to provide the type to fetchImages by parameters (see payload):
export const fetchImages = createAsyncThunk('images/fetchImages', async (payload) => {
const res = await axios.get(
`https://api.pexels.com/v1/search?query=${payload.type}&per_page=15`,
{
headers: config,
}
);
return res.data;
});
Then you need to provide the type to the function, when calling fetchImages. Here in an example component:
import { fetchImages } from '../redux/imageSlice';
import { useDispatch, useSelector } from 'react-redux';
function YourCallingComponent(props) {
const dispatch = useDispatch();
const currentType = useSelector((state) => state.type);
function fetchImagesOfType() {
dispatch(fetchImages({
type: currentType
}));
}
return (
...
)
}

React Redux not displaying the data when trying to dispatch (Firestore)

I'm trying to use React Redux on my application. I will be building something like instagram, so the scale of the application is going to be big. I have implement redux correctly (I believe) although I'm not getting the fetch result from firestore on the dispatch function, if I insert the same code before the return it works.
action/index.ts:
import { db, auth } from '../../services/firebase';
import { USER_STATE_CHANGE } from '../constants';
export function fetchUser() {
return (dispatch: any) => {
db.collection('users')
.doc(auth.currentUser!.uid)
.get()
.then((snapshot) => {
if (snapshot.exists) {
console.log(snapshot)
dispatch({
type: USER_STATE_CHANGE,
payload: snapshot.data(),
});
}
});
};
}
constants/index.ts:
export const USER_STATE_CHANGE = 'USER_STATE_CHANGE';
reducers/index.ts:
import { combineReducers } from 'redux';
import { user } from './user';
const Reducers = combineReducers({
userState: user,
});
export default Reducers;
reducers/user.ts:
const initialState = {
currentUser: null,
};
export const user = (state = initialState, action: any) => {
return {
...state,
currentUser: action.currentUser,
};
};
App.tsx:
// Redux
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import rootReducer from './src/redux/reducers';
import thunk from 'redux-thunk';
const store = createStore(rootReducer, applyMiddleware(thunk));
export default function App() {
if (!fontsLoaded) return <></>;
return (
<ThemeProvider theme={theme}>
<Provider store={store}>
<Main />
</Provider>
<ToastMessage />
</ThemeProvider>
);
}
Main.tsx:
import React, { useEffect, useState } from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { StatusBar } from 'expo-status-bar';
import { AuthRoutes } from './routes/auth.routes';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { fetchUser } from './redux/actions';
import { auth } from './services/firebase';
function Main() {
const [isAuthenticated, setIsAuthenticated] = useState(false);
useEffect(() => {
fetchUser();
auth.onAuthStateChanged((user) => {
if (user) setIsAuthenticated(true);
});
}, []);
return (
<NavigationContainer>
<AuthRoutes isAuthenticated={isAuthenticated} setIsAuthenticated={setIsAuthenticated} />
<StatusBar style="auto" />
</NavigationContainer>
);
}
const mapDispatchProps = (dispatch: any) => bindActionCreators({ fetchUser }, dispatch);
export default connect(null, mapDispatchProps)(Main);
The fetchUser() is called here, and should log the data from firestore, although I don't get any data logged.
The function is beeing called:
on reducer/user.ts
Have you try to do this:
const initialState = {
currentUser: null,
};
export const user = (state = initialState, action: any) => {
return {
...state,
currentUser: action.payload,
};
};
Replace this
export const user = (state = initialState, action: any) => {
return {
...state,
currentUser: action.currentUser,
};
};
With this
export const user = (state = initialState, action: any) => {
return {
...state,
currentUser: action.payload,
};
};
Because you are setting data in payload but you are accessing from currentUser which empty. Either replace payload with currentUser in useDispatch or replace currentUser with payload in reducer.

useSelector Returning Undefined object

I am trying to store users fetched by random users api and trying to dispatch it to my store i have store users with an empty array, when i am trying to get users using useSelector i am getting an undefined object.
Here is my store.js:
import { configureStore } from "#reduxjs/toolkit";
import counterReducer from "../features/counter/counterSlice";
import userReducer from "../features/users/userSlice";
export const store = configureStore({
reducer: {
counter: counterReducer,
user: userReducer,
},
});
Here is userSlice.js
import { createSlice } from "#reduxjs/toolkit";
export const userSlice = createSlice({
name: "users",
initialState: {
userArray: [],
},
reducers: {
getUsers: (state, action) => {
state.userArray = action.payload;
}
}
})
export const { getUsers } = userSlice.actions;
export const selectUsers = (state) => state.users.userArray;
export default userSlice.reducer;
Here is App.js
import logo from './logo.svg';
import './App.css';
import {useSelector, useDispatch } from 'react-redux';
import { getUsers, selectUsers } from './features/users/userSlice';
function App() {
const dispatch = useDispatch();
const users = useSelector(selectUsers);
const fetchUsers = async () => {
fetch("https://gorest.co.in/public/v2/users")
.then((response) => response.json())
.then((data) => {
console.log("data=====", data);
dispatch(getUsers(data));
});
};
return (
<div className="App">
<header className="App-header">
<button onClick={fetchUsers}>Get Users</button>
{users.length > 0 &&
users.map((user) => {
<li>user.name</li>;
})}
</header>
</div>
);
}
export default App;
you are mixing up state.user and state.users
either rename user in configureStore to users or use state.user.userArray

useSelector not updating even after dispatch

I'm trying to display products using the fetched axios result from reducer, but the useSelector value just won't change and is still empty even after dispatch. I have checked the axios result and the response has correct data. Does it have something to do with this line on redux documentation?
With useSelector(), returning a new object every time will always force a re-render by default.
reducer
import axios from "axios";
export const products = (state = [], action) => {
switch (action.type) {
case "FETCH_PRODUCTS": {
const uri = "/products";
axios.get(uri).then(function (response) {
if (response.status == 200) {
console.log(response.data.products); // ===> correct new value
return { state: response.data.products };
}
});
}
App.js
import React, { useEffect } from "react";
import { shallowEqual, useSelector, useDispatch } from "react-redux";
import "../css/App.css";
import { Products, Navbar, Cart } from "../components";
function App() {
const dispatch = useDispatch();
const products = useSelector((state) => state.products, shallowEqual);
const cart = useSelector((state) => state.cart, shallowEqual);
useEffect(() => {
dispatch({
type: "FETCH_PRODUCTS",
});
console.log(products); // ===> still empty array
}, []);
return (
<div className="App">
<Navbar />
<Cart cart={cart} />
<Products products={products} />
</div>
);
}
export default App;
You should first create your action
import axios from 'axios';
export const fetchProducts = () => async (dispatch) => {
try {
const { data } = await axios.get('...');
dispatch({ type: "FETCH_PRODUCTS", payload: data.result });
} catch (error) {
console.log(error);
}
};
Then, Use dispatch and action together
import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchProducts } from './actions';
const getSelectors = state => ({ cart: state.cart, products: state.products });
const App = () => {
const dispatch = useDispatch();
const {cart, products} = useSelector(getSelectors);
useEffect(() => {
dispatch(fetchProducts());
}, []);
return (
<div className="App">
<Navbar />
<Cart cart={cart} />
<Products products={products} />
</div>
);
};

react-redux state is changing but display is stills same (no re-render)

i've been working on a project trying to learn redux with react. But there is an error and i don't know exactly how to fix it. Files/codes in down if you need more information about how store works.
store/index.js
import { createStore, applyMiddleware, compose } from "redux";
import rootReducer from "./reducers";
import thunk from 'redux-thunk'
const middlewares = [thunk]
const store = createStore(rootReducer, compose(applyMiddleware(...middlewares), window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()))
export default store;
actions/index.js
import axios from "axios"
export const increment = (payload) => {
return {
type: 'INCREMENT',
payload: payload
}
}
export const decrement = () => {
return {
type: 'DECREMENT'
}
}
export const fetch = () => {
return async (dispatch) => {
axios.get('https://jsonplaceholder.typicode.com/posts/')
.then(data => dispatch({type: 'FETCH', payload: data.data}))
}
}
store/todos.js
const todos = (state = [], action) => {
switch(action.type){
case 'FETCH':
return Object.assign(state, action.payload);
default:
return state;
}
}
export default todos;
App.js
import logo from './logo.svg';
import './App.css';
import {useSelector, useDispatch, connect} from 'react-redux'
import {increment, decrement, fetch} from './store/actions/'
import { GeistProvider, CssBaseline } from '#geist-ui/react';
function App(props) {
const count = useSelector((state) => state.counter)
const todos = useSelector((state) => state.todos)
const dispatch = useDispatch()
return (
<div className="App">
<header className="App-header">
<h1>Count is {count}</h1>
<button onClick={() => dispatch(increment(3))}>+</button>
<button onClick={() => dispatch(decrement())}>-</button>
<button onClick={() => dispatch(fetch())}>FETCH</button>
{todos.length ? todos[0].title : <h1>Not fetched.</h1>}
</header>
</div>
);
}
export default App;
This is the codes in project. Let me know if you guys need more information about anything. Thanks for help!
You are wrong at return Object.assign(state, action.payload);. It's mutated state so redux can't detect state change. You should read this https://redux.js.org/understanding/thinking-in-redux/three-principles#changes-are-made-with-pure-functions
You can change to this
return Object.assign({}, state, action.payload);
or this
return { ...state, ...action.payload }

Categories

Resources