After clicking the "Add client" button, the state changes, but the element is not render
I started learning redux recently. I'm trying to understand the logic of the framework. Therefore, I decided to make a bank emulation. You can add money and get it. With clients the same functionality. But for some reason the added clients are not rendered
I think the problem in my App.js.
My App.js
import { useDispatch, useSelector } from "react-redux";
import "./App.css";
function App() {
const dispatch = useDispatch();
const cash = useSelector((state) => state.cash.cash);
const customers = useSelector((state) => state.customer.customers);
const addCash = (cash) => {
dispatch({ type: "ADD_CASH", payload: cash });
};
const getCash = (cash) => {
dispatch({ type: "GET_CASH", payload: cash });
};
const addCustomer = (name) => {
const customer = {
name,
id: Date.now(),
};
dispatch({ type: "ADD_CUSTOMER", payload: customer });
};
return (
<div className="App">
<div style={{ display: "flex" }}>
<button onClick={() => addCash(Number(prompt()))}>Add cash</button>
<button onClick={() => getCash(Number(prompt()))}>Get cash</button>
</div>
<div style={{ fontSize: "2rem" }}>{cash}</div>
{customers.length > 0 ? (
<div>
{customers.map((customer) => {
<div style={{ fontSize: "2rem" }}>{customer.name}</div>;
})}
</div>
) : (
<div style={{ fontSize: "2rem" }}>No clients</div>
)}
<div style={{ display: "flex" }}>
<button onClick={() => addCustomer(prompt())}>Add client</button>
{/* <button onClick={() => getCash(Number(prompt()))}>Get client</button> */}
</div>
</div>
);
}
export default App;
My store
import { createStore, combineReducers } from "redux";
import { cashReducer } from "./cashReducer";
import { customerReducer } from "./customerReducer";
import { composeWithDevTools } from "redux-devtools-extension";
const rootReducer = combineReducers({
cash: cashReducer,
customer: customerReducer,
});
export const store = createStore(rootReducer, composeWithDevTools());
**My sustomer reducer**
const defaultState = {
customers: [],
};
export const customerReducer = (state = defaultState, action) => {
switch (action.type) {
case "ADD_CUSTOMER":
return { ...state, customers: [...state.customers, action.payload] };
case "REMOVE_CUSTOMERS":
return { ...state, cash: state.cash - action.payload };
default:
return state;
}
};
My cash reducer
const defaultState = {
cash: 5,
};
export const cashReducer = (state = defaultState, action) => {
switch (action.type) {
case "ADD_CASH":
return { ...state, cash: state.cash + action.payload };
case "GET_CASH":
return { ...state, cash: state.cash - action.payload };
default:
return state;
}
};
And my index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { store } from './store';
import { Provider } from 'react-redux';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
reportWebVitals();
//this
{customers.map((customer) => {
<div style={{ fontSize: "2rem" }}>{customer.name}</div>;
})}
//should be this... with parenthesis instead of curly braces around the div
{customers.map((customer) => (
<div style={{ fontSize: "2rem" }}>{customer.name}</div>;
))}
Related
I am trying to fetch the local json data and then using it inside my component.
I have mentioned the extraReducers while keeping the reducer empty. Below is the code,
import { createAsyncThunk, configureStore } from "#reduxjs/toolkit";
export const fetchData = createAsyncThunk('data/fetch', async () =>{
const response = await fetch('../src/assets/relatives.json');
const data = await response.json();
return data;
})
const dataSlice = {
name: 'data',
initialState: [],
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchData.pending, (state) => {
state.status = 'loading';
})
.addCase(fetchData.fulfilled, (state, action) => {
state.status = 'succeeded';
state.data = action.payload;
})
.addCase(fetchData.rejected, (state, action) => {
state.status = 'failed';
state.error = action.error.message;
});
},
};
const store = configureStore({
reducer:{
data: dataSlice,
},
preloadedState: {data:[]},
})
export default store;
This is my component:
import React, { useEffect } from "react";
import Card from "#mui/material/Card";
import CardActions from "#mui/material/CardActions";
import CardContent from "#mui/material/CardContent";
import Button from "#mui/material/Button";
import Typography from "#mui/material/Typography";
import TreeView from "#mui/lab/TreeView";
import { ExpandMore ,ChevronRight } from "#mui/icons-material";
import TreeItem from "#mui/lab/TreeItem";
import { useDispatch, useSelector } from "react-redux";
import { fetchData } from "../configureStore";
const FamilyFolder = () => {
const dispatch = useDispatch();
const data = useSelector((state = state.data.data));
const status = useSelector((state) => state.data.status);
useEffect(() => {
dispatch(fetchData());
}, [dispatch]);
if (status === "loading") {
return<div>Loading...</div>
}
if (status === 'failed') {
return <div>Error</div>;
}
console.log(data);
return (
<div>
<Card sx={{ minWidth: 50 }}>
<CardContent>
<Typography
sx={{ fontSize: 14, align: "center" }}
color="text.secondary"
gutterBottom
>
Family Tree
</Typography>
</CardContent>
<TreeView
aria-label="file system navigator"
defaultCollapseIcon={<ExpandMore />}
defaultExpandIcon={<ChevronRight />}
sx={{ height: 240, flexGrow: 1, maxWidth: 400, overflowY: "auto" }}
>
<TreeItem nodeId="1" label="gg">
<TreeItem nodeId="2" label="Calendar" />
</TreeItem>
<TreeItem nodeId="5" label="Documents">
<TreeItem nodeId="10" label="OSS" />
<TreeItem nodeId="6" label="MUI">
<TreeItem nodeId="8" label="index.js" />
</TreeItem>
</TreeItem>
</TreeView>
<CardActions className="parent-button-container">
<CardActions className="">
<Button variant="contained">Import JSON</Button>
<Button variant="contained">Add Family</Button>
</CardActions>
<CardActions className="">
<Button variant="contained">Export JSON</Button>
<Button variant="contained">Print Tree</Button>
</CardActions>
</CardActions>
</Card>
</div>
);
};
export default FamilyFolder;
I have added the preloadedState inside the store as initial state but that is also not working.
Should i mention anything inside reducer: {} ?. If yes, please tell what i should do
I am working with MongoDB, Express, React, Node (MERN) project. I having the issue of "Cannot read property 'map' of undefined after changing the redux files to achieve loading effect from Material UI/core <CircularProgress />
I have tried to access data by useSelector in different ways, even with shallowEqual method. I tried to call the getStudents() inside DashBoardAdmin also. While also tried with useEffect to dispatch (getStudents()) with dependencies array. All didn't worked so far. Then tried to debug on inspect section of chrome, where I found that on first reload of page, there happens to get data from backend on action.payload but it isn't able to populate into the state as whole. That's might be reason that useSelector is getting an empty array & providing "Cannot read property 'map' of undefined
I assume, something went wrong after the students.js file of reducers, after introducing an object in the state. I am trying my best to debug.
My index.js file :
import React from "react";
import ReactDOM from "react-dom";
import "./Styles/index.css";
import App from "./App";
import { Provider } from "react-redux";
import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
import { reducers } from "./redux/reducers/index";
const composeEnhancers =
typeof window === "object" && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({})
: compose;
const enhancer = composeEnhancers(compose(applyMiddleware(thunk)));
const store = createStore(reducers, enhancer);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
My app.js file:
import React, { useEffect, useState } from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import "./Styles/App.css";
import AdminSignIn from "./Pages/AdminSignIn";
import DashBoardAdmin from "./Pages/Admin";
import NavbarAdmin from "./Navbars/NavbarAdmin";
import BottomNavbar from "./Navbars/bottomNavbar";
import { useDispatch } from "react-redux";
import { Typography } from "#material-ui/core";
import { NotFound } from "./Not_Found/NotFound";
import { getStudents } from "./redux/actions/studentAction";
function App() {
const user = JSON.parse(localStorage.getItem("account"));
const dispatch = useDispatch();
useEffect(() => {
dispatch(getStudents());
}, [dispatch]);
return (
<>
<Router>
{user?.result?._id ? (
<NavbarAdmin />
) : (
<Typography variant="h2">{"Fetch"} Organization Name</Typography>)}
<Switch>
<Route path="/" exact>
<AdminSignIn />
</Route>
<Route path="/dashboard" exact>
<DashBoardAdmin />
</Route>
<Route >
<NotFound />
</Route>
</Switch>
<BottomNavbar />
</Router>
</>
);
}
export default App;
My DashBoardAdmin.js file:
import { Box, Button, Card, CardHeader, Chip, CircularProgress, Divider, Table, TableBody, TableCell, TableHead, TableRow, TableSortLabel, Tooltip} from "#material-ui/core";
import { Link } from 'react-router-dom'
import ArrowRightIcon from "#material-ui/icons/ArrowRight";
import moment from "moment";
import PerfectScrollbar from "react-perfect-scrollbar";
import { useSelector } from "react-redux";
const DashBoardAdmin = () => {
const { students, isLoading } = useSelector((state) => state.students);
return (
<div className="padding-grid">
<Card>
<CardHeader title="Latest updates on students" />
<Divider />
<PerfectScrollbar>
<Box sx={{ minWidth: 800 }}>
<Table>
<TableHead>
<TableRow>
<TableCell>Roll Number</TableCell>
<TableCell>Name of student</TableCell>
<TableCell sortDirection="desc">
<Tooltip enterDelay={300} title="Sort">
<TableSortLabel active direction="desc">
Date of Admission
</TableSortLabel>
</Tooltip>
</TableCell>
<TableCell>Status</TableCell>
</TableRow>
</TableHead>
{isLoading ? (
<CircularProgress />
) : (
<TableBody>
{students.map((stu) => (
<TableRow hover key={stu.id}>
<TableCell>{stu.rollNumber}</TableCell>
<TableCell>{stu.firstName} {" "} {stu.lastName} + {" "} {stu.surname}</TableCell>
<TableCell>
{moment(stu.createdAt).format("DD/MM/YYYY")}
</TableCell>
<TableCell>
<Chip color="primary" label={stu.status} size="small" />
</TableCell>
</TableRow>
))}
</TableBody>
)}
</Table>
</Box>
</PerfectScrollbar>
<Box
sx={{
display: "flex",
justifyContent: "flex-end",
p: 2,
}}>
<Link to="/students-info">
<Button
color="primary"
endIcon={<ArrowRightIcon />}
size="small"
variant="text">
View all
</Button>
</Link>
</Box>
</Card>
</div>
);
};
export default DashBoardAdmin;
My redux studentAction.js file:
import { FETCH_STUDENTS, START_LOADING, END_LOADING } from "../constants/actionTypes";
import * as api from "../api/index.js";
export const getStudents = () => async (dispatch) => {
try {
dispatch({ type: START_LOADING })
const { data } = await api.fetchStudents();
dispatch({ type: FETCH_STUDENTS, payload: data });
dispatch({ type: END_LOADING})
} catch (error) {
console.log(error);
}
};
My API index.js file:
import axios from "axios";
const API = axios.create({ baseURL: "http://localhost:5000" });
API.interceptors.request.use((req) => {
if (localStorage.getItem("account")) {
req.headers.Authorization = `Bearer ${
JSON.parse(localStorage.getItem("account")).token
}`;
}
return req;
});
export const fetchStudents = () => API.get("/students-info");
My reducers by students.js and best guess is that something is wrong here or it started after I included isLoading:
import { FETCH_STUDENTS, START_LOADING, END_LOADING } from "../constants/actionTypes";
function students(state = { isLoading: true, students: [] }, action) {
switch (action.type) {
case START_LOADING:
return { ...state, isLoading: true };
case END_LOADING:
return { ...state, isLoading: false };
case FETCH_STUDENTS:
return { ...state, students: action.payload.data };
default:
return state;
}
}
export default students;
index.js combine reducer file:
import { combineReducers } from "redux";
import students from "./students";
import auth from "./auth";
export const reducers = combineReducers({ students, auth });
Errors which I am getting are:
Uncaught TypeError: Cannot read property 'map' of undefined
at DashBoardAdmin (DashBoardAdmin.js:51)
at renderWithHooks (react-dom.development.js:14985)
at updateFunctionComponent (react-dom.development.js:17356)
at beginWork (react-dom.development.js:19063)
at HTMLUnknownElement.callCallback (react-dom.development.js:3945)
at Object.invokeGuardedCallbackDev (react-dom.development.js:3994)
at invokeGuardedCallback (react-dom.development.js:4056)
at beginWork$1 (react-dom.development.js:23964)
at performUnitOfWork (react-dom.development.js:22776)
at workLoopSync (react-dom.development.js:22707)
at renderRootSync (react-dom.development.js:22670)
at performSyncWorkOnRoot (react-dom.development.js:22293)
at react-dom.development.js:11327
at unstable_runWithPriority (scheduler.development.js:468)
at runWithPriority$1 (react-dom.development.js:11276)
at flushSyncCallbackQueueImpl (react-dom.development.js:11322)
at flushSyncCallbackQueue (react-dom.development.js:11309)
at batchedUpdates$1 (react-dom.development.js:22387)
at Object.notify (Subscription.js:19)
at Subscription.notifyNestedSubs (Subscription.js:90)
at Subscription.handleChangeWrapper (Subscription.js:95)
at Object.dispatch (redux.js:297)
at dispatch (<anonymous>:3856:17)
at index.js:11
at dispatch (redux.js:659)
at studentAction.js:35
and another error:
Warning: validateDOMNesting(...): <div> cannot appear as a child of <table>.
at div
at CircularProgress (http://localhost:4000/static/js/vendors~main.chunk.js:80761:23)
at WithStyles (http://localhost:4000/static/js/vendors~main.chunk.js:119309:31)
at table
at Table (http://localhost:4000/static/js/vendors~main.chunk.js:102171:23)
at WithStyles (http://localhost:4000/static/js/vendors~main.chunk.js:119309:31)
at div
at StyledComponent (http://localhost:4000/static/js/vendors~main.chunk.js:119080:28)
at div
at ScrollBar (http://localhost:4000/static/js/vendors~main.chunk.js:231982:5)
at div
at Paper (http://localhost:4000/static/js/vendors~main.chunk.js:94231:23)
I am getting data from backend by using simple syntax of redux in the students.js file :
import { FETCH_STUDENTS } from "../constants/actionTypes";
export default (students = [], action) => {
switch (action.type) {
case FETCH_STUDENTS:
return action.payload;
default:
return students;
}
};
Need to get an alternative method of implementing isLoading or START_LOADING / END_LOADING dispatch to the UI
ok after seeing your app.js I see you are using thunk and I think you have to use functions rather than passing objects directly.
Also I added an action to get the error just in case
studentAction.js
import {
FETCH_STUDENTS,
START_LOADING,
END_LOADING,
} from "../constants/actionTypes";
import * as api from "../api";
const startLoading = () => {
return {
type: START_LOADING,
};
};
const fetchStudents = (data) => {
return {
type: FETCH_STUDENTS,
data,
};
};
const endLoading = (error) => {
return {
type: END_LOADING,
error,
};
};
export const getStudents = () => {
return async (dispatch) => {
dispatch(startLoading());
try {
const { data } = await api.fetchStudents()
dispatch(fetchStudents(data))
} catch (error) {
dispatch(endLoading(error.message));
}
};
};
students.js
import {
FETCH_STUDENTS,
START_LOADING,
END_LOADING,
} from "../../constants/actionTypes";
const initialState = {
isLoading: false,
error: "",
students: [],
};
function students(state = initialState, action) {
switch (action.type) {
case START_LOADING:
return { ...state, isLoading: true };
case END_LOADING:
return { ...state, isLoading: false, students: [], error: action.error };
case FETCH_STUDENTS:
return {
...state,
isLoading: false,
students: action.data,
};
default:
return state;
}
}
export default students;
your app.js and index.js is correct for me
and in your Admin.js you can use this condition
import { useSelector } from "react-redux";
const DashBoardAdmin = () => {
const { students, isLoading, error } = useSelector((state) => state.students);
return (
<>
{error === "" && (
<div className="padding-grid">
...
{isLoading || !students.length > 0 ? (
<h1>loading...</h1>
) : (
<div>
{students.map((stu) => (
<p key={stu.id}>{JSON.stringify(stu)}</p>
))}
</div>
)}
...
</div>
)}
</>
);
};
export default DashBoardAdmin;
FoodList.js
import React, { Component } from "react";
import { View, Text, FlatList } from "react-native";
import { Content, List, ListItem } from "native-base";
import { useSelector, useDispatch } from "react-redux";
import { deleteFood } from "../../redux/actions/food";
const FoodList = () => {
const dispatch = useDispatch();
const deleteCurrent = (key) => dispatch(deleteFood(key));
const foods = useSelector((state) => state.foodReducer.FoodList); <------------
return (
<FlatList
data={foods}
keyExtractor={(item, index) => item.key.toString()}
renderItem={(data) => <ListItem title={data.item.name} />}
/>
);
};
export default FoodList;
FoodCreate.js
import { useDispatch } from "react-redux";
import { addFood } from "../../redux/actions/food";
const FoodCreate = ({ navigation: { goBack } }) => {
const [food, setFood] = useState("");
const dispatch = useDispatch();
const submitFood = (food) => dispatch(addFodd(food));
return (
<Container>
<Header>
<Left>
<Button transparent>
<Icon
name="arrow-back"
onPress={() => this.props.navigation.goBack()}
style={{ fontSize: 25, color: "red" }}
/>
</Button>
</Left>
<Body>
<Title>Add Food</Title>
</Body>
<Right>
<Button transparent>
<Icon
name="checkmark"
style={{ fontSize: 25, color: "red" }}
onPress={() => {
submitFood(food);
setFood("");
}}
/>
</Button>
</Right>
</Header>
<View style={{ alignItems: "center", top: hp("3%") }}>
<TextInput
placeholder="Food Name"
placeholderTextColor="white"
style={styles.inptFood}
value={food}
onChangeText={(food) => setFood(food)}
/>
</View>
actions/food.js
import { ADD_FOOD, DELETE_FOOD } from "./types";
export const addFood = (food) => ({
type: ADD_FOOD,
data: food,
});
export const deleteFood = (key) => ({
type: DELETE_FOOD,
key: key,
});
store/store.js
import { createStore, combineReducers } from "redux";
import foodReducer from "../reducers/foodReducer";
const rootReducer = combineReducers({
foodReducer: foodReducer,
});
const configureStore = () => createStore(rootReducer);
export default configureStore;
actions/types.js
export const ADD_FOOD = "ADD_FOOD";
export const DELETE_FOOD = "DELETE_FOOD";
reducers/foodReducer.js
import { ADD_FOOD, DELETE_FOOD } from "../actions/types";
const initialState = {
FoodList: [],
};
const foodReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_FOOD:
return {
...state,
FoodList: state.FoodList.concat({
key: Math.random(),
name: action.data,
}),
};
case DELETE_FOOD:
return {
...state,
FoodList: state.FoodList.filter((item) => item.key != action.key),
};
default:
return state;
}
};
export default foodReducer;
App.js
import FoodListScreen from "./components/Main/FoodList.js";
import configureStore from "./redux/store/store";
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
const store =
(createStore(rootReducer, applyMiddleware(thunk)), configureStore());
return (
<Provider store={store}>
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="FoodCreate"
component={FoodCreateScreen}
options={{ headerShown: false }}
/>
<Stack.Screen
name="FoodList"
component={FoodListScreen}
options={{ headerShown: false }}
/>
</Stack.Navigator>
</NavigationContainer>
</Provider>
Hey everyone, I'm new to redux and I'm trying to use it my Diet App, the user creates a Food item in FoodCreate.js and it will be listed in FoodList.js, at the moment when I run the code it gives me back the following error: state.foodReducer.FoodList is undefined, I put an arrow next to the line of code that gives me the error, I used this approach by following the following YouTube tutorial: https://www.youtube.com/watch?v=jTJ6zo5GO7E, thank in advance for your help.
ok maybe i found
//In your store/store.js
import { createStore, combineReducers,applyMiddleware } from "redux";
import thunk from 'redux-thunk'
import foodReducer from "../reducers/foodReducer";
const configureStore = () => {
const store = createStore(
combineReducers({
foodReducer: foodReducer,
}),
applyMiddleware(thunk)
);
return store;
}
export default configureStore;
//In your App.js
...
import configureStore from "./redux/store/store";
const store = ConfigureStore();
return(
<Provider store={store}>
...
</Provider>
)
I am developing a react-native application using redux. This is my app.js:
import 'react-native-gesture-handler';
import React from 'react';
import {Provider} from 'react-redux';
import AsyncStorage from '#react-native-community/async-storage';
import {DefaultTheme, Provider as PaperProvider} from 'react-native-paper';
import {NavigationContainer} from '#react-navigation/native';
import {createBottomTabNavigator} from '#react-navigation/bottom-tabs';
import Icon from 'react-native-vector-icons/Ionicons';
import AppHeader from './components/ui/AppHeader';
import Home from './views/Home';
import Login from './views/Login';
import MyCoupons from './views/MyCoupons';
import ShoppingCart from './views/ShoppingCart';
import Signup from './views/Signup';
import Intro from './views/Intro';
import reducers from './reducers/index';
import configureStore from './store';
export const store = configureStore(reducers);
const Tab = createBottomTabNavigator();
const App = () => {
let localStorageUser = null;
const getLocalStorageUser = async () => {
try {
localStorageUser = await AsyncStorage.getItem('user');
} catch (error) {
console.log(error);
}
};
const getInitialRouteName = () => {
switch(localStorageUser) {
case null: return 'Cuenta'
case '': return 'Cuenta'
default: return 'Home'
}
}
return (
<>
<AppHeader />
<Provider store={store}>
<PaperProvider>
<NavigationContainer>
<Tab.Navigator
initialRouteName={getInitialRouteName()}
screenOptions={({route}) => ({
tabBarIcon: ({focused, color, size}) => {
let iconName;
if (route.name == 'Home') {
iconName = focused? 'home': 'home-outline';
} else if (route.name == 'Mis Cupones') {
iconName = focused? 'film': 'film-outline';
} else if (route.name == 'Carrito') {
iconName = focused? 'cart': 'cart-outline';
} else if (route.name == 'Cuenta') {
iconName = focused? 'person-circle': 'person-circle-outline';
}
return <Icon name={iconName} color='blue' size={25} />
}
})}
>
<Tab.Screen name='Home' component={Home} />
<Tab.Screen name='Carrito' component={ShoppingCart} />
<Tab.Screen name='Mis Cupones' component={MyCoupons} />
<Tab.Screen name='Cuenta' component={Login}/>
</Tab.Navigator>
</NavigationContainer>
</PaperProvider>
</Provider>
</>
);
};
export default App;
This is my store/index.js:
import {applyMiddleware, compose, createStore} from 'redux';
import reducers from '../reducers/index';
import {routerMiddleware} from 'connected-react-router';
import createSagaMiddleware from 'redux-saga';
import rootSaga from '../sagas/index';
import {Provider} from 'react-redux';
import AsyncStorage from '#react-native-community/async-storage';
const sagaMiddleware = createSagaMiddleware();
const middlewares = [sagaMiddleware];
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
export default function configureStore(initialState) {
const store = createStore(initialState,
composeEnhancers(applyMiddleware(...middlewares)));
sagaMiddleware.run(rootSaga);
if (module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept('../reducers/index', () => {
const nextRootReducer = require('../reducers/index');
store.replaceReducer(nextRootReducer);
});
}
return store;
}
This is my reducers/index.js:
import {combineReducers} from 'redux';
import Auth from './Auth';
export default () => combineReducers({
auth: Auth,
});
This is my reducers/Auth.js:
import {
GET_USER_SUCCESS,
LOGIN_SUCCESS,
SET_USER,
} from '../types';
const INIT_STATE = {
username: null,
token: null,
userInfo: null,
};
export default (state = INIT_STATE, action) => {
switch (action.type) {
case LOGIN_SUCCESS: {
return {
...state,
username: action.payload.username,
token: action.payload.token
}
}
case GET_USER_SUCCESS: {
return {
...state,
userInfo: Object.assign({}, action.payload.userInfo)
}
}
case SET_USER: {
return {
...state,
username: null,
token: null,
userInfo: null,
}
}
default:
return state
}
}
And this is views/Login.js, where I try to connect to the store:
import React from 'react';
import {View, StyleSheet} from 'react-native';
import {Button, TextInput, Headline} from 'react-native-paper';
import globalStyles from '../styles/global';
import {connect} from 'react-redux';
import AsyncStorage from '#react-native-community/async-storage';
import {login} from '../actions/Auth';
class Login extends React.Component {
constructor (props) {
super();
this.state = {
email: '',
password: '',
loggedIn: false
};
}
render () {
const setLocalStorageUser = async (user) => {
try {
await AsyncStorage.setItem('user', user);
} catch (error) {
console.log(error);
}
};
const handleNewUserPress = () => {
navigation.navigate('Signup');
}
const handleLoginPress = async () => {
login(this.state.email, this.state.password);
navigation.navigate('Home');
}
const handleChange = (name, text) => {
this.setState({[name]: value});
}
return (
<View style={globalStyles.container}>
<TextInput style={styles.input} value={this.state.email} label="Email" onChangeText={(text) => handleChange('email', text)} />
<TextInput style={styles.input} value={this.state.password} label="ContraseƱa" onChangeText={(text) => handleChange('password', text)} />
<Button
style={styles.button}
mode='contained'
onPress={() => handleLoginPress()}
disabled={this.state.email=='' || this.state.password==''}
>
Enviar
</Button>
<Button color='blue' icon="plus-circle" onPress={() => handleNewUserPress()}>
Nuevo Usuario
</Button>
</View>
);
}
}
const styles = StyleSheet.create({
input: {
marginBottom: 20,
backgroundColor: 'darkgray'
},
button: {
color: 'white',
backgroundColor: 'blue',
marginBottom: 20
}
})
const mapStateToProps = ({auth}) => {
const {username, token} = auth;
return {username, token};
}
const mapDispatchToProps = {
login,
}
export default connect(mapStateToProps, mapDispatchToProps)(Login);
The problem is that this arrange is raising this exceptio:
I have checked the code several times and I haven't been able to find my mistake.
Your issue is in here:
export default () => combineReducers({
auth: Auth,
});
now your reducers you supply to createStore is a function ... not the combined-reducers
This should be
export default combineReducers({
auth: Auth,
});
I currently have this practice code that uses Context and Memo Hooks API
codesandbox
Here is a code snippet
export const InputContext = createContext()
export const ParentProvider = ({ initialValues, children }) => {
console.log(initialValues)
const [values, setValues ] = useState(initialValues);
const value = {
values,
setValues
}
return <InputContext.Provider value={value}>{children}</InputContext.Provider>
}
What I want is to update the value of array that holds indicators using Context API after I click edit.
The problem is that I can't access the Context after accessing through the memo
You'll need to wrap all components that need access to the context with the provider. Something like this...
ParentProvider.js
import React, { createContext, useState } from "react";
const INITIAL_STATE = [];
export const InputContext = createContext(INITIAL_STATE);
export const ParentProvider = ({ children }) => {
const [values, setValues] = useState(INITIAL_STATE);
React.useEffect(() => {
console.log("[parentprovider.js]::new values", values);
}, [values]);
return (
<InputContext.Provider value={{ values, setValues }}>
{children}
</InputContext.Provider>
);
};
ShowIndicator.js
import React, { memo, useContext, useState } from "react";
import { Button } from "react-bootstrap";
import { InputContext } from "./ParentProvider";
const ShowIndicator = memo(
({ name, context }) => {
const [_name, _setName] = useState(name);
const [text, setText] = useState();
const { values, setValues } = useContext(InputContext);
const editData = e => {
let newValues = [...values];
newValues[values.indexOf(_name)] = text;
setValues(newValues);
_setName(text);
};
const handleTextChange = e => setText(e.target.value);
const renderDatas = () => {
return (
<div key={_name} className="d-flex justify-content-between">
<input
className="d-flex align-items-center"
defaultValue={_name}
onChange={handleTextChange}
/>
<div>
<Button
variant="info"
style={{ marginRight: "10px" }}
onClick={editData}
>
Edit
</Button>
<Button variant="dark">Delete</Button>
</div>
</div>
);
};
return <div style={{ marginBottom: "5px" }}>{renderDatas()}</div>;
},
(prev, next) => prev.value === next.value
);
export default ShowIndicator;
App.js
import React, { useState, useContext } from "react";
import "./styles.css";
import { Form, Button, Container } from "react-bootstrap";
import ShowIndicator from "./ShowIndicator";
import { InputContext } from "./ParentProvider";
function App() {
const [curText, setCurText] = useState("");
const { values, setValues } = useContext(InputContext);
const onSubmit = e => {
e.preventDefault();
if (!values.includes(curText)) {
values ? setValues([...values, curText]) : setValues([curText]);
setCurText("");
}
};
const onChange = e => setCurText(e.target.value);
return (
<div>
<Container style={{ marginTop: "10px", textAlign: "center" }}>
<div>Add Indicator</div>
<Form inline onSubmit={onSubmit} style={{ marginBottom: "1rem" }}>
<Form.Control
style={{ flex: "1 0 0" }}
onChange={onChange}
value={curText}
/>
<Button type="submit" variant="success">
Submit
</Button>
</Form>
{values &&
values.map((data, index) => {
return <ShowIndicator key={index} name={data} />;
})}
</Container>
</div>
);
}
export default App;
index.js
import React from "react";
import App from "./App";
import { render } from "react-dom";
import { ParentProvider } from "./ParentProvider";
render(
<ParentProvider>
<App />
</ParentProvider>,
document.getElementById("root")
);
Using useContext you need to pass the entire context object (not only Consumer). And just use it like this
const Component = () =>{
const context = useContext(InputContext)
const { values, setValues } = context
const handleChange = () => setValues('foo')
return(
<>
{values}
<button onClick={handleChange}>Change</button>
</>
)
}