How can I render only once an Alert component in react native? - javascript

How can I render only one time an Alert component in react native if my dashboardScreen is being render many times?
I'm using Context api, if I dispatch an action it causes another render, if I use a useState it causes another render/renders, any idea will be highly appreciate it.
**AppContext.js**
export const AppContext = createContext();
export const AppProvider = ({ children })=> {
const [ state, dispatch ] = useReducer( appReducer, appInicialState);
return (
<AppProvider.Provider value={{
...state,
}}>
{ children }
</AppProvider.Provider>
)
}
**Dashboard.js**
export const Dashboard = () => {
const { state, dispatch } = useContext( AppContext );
const [showAlert, setShowAlert] = useState(true);
const handleCancelPressed = () => {
dispatch({
type: 'cancelPressed',
payload: {
userInfo: false,
}
});
}
const handleAcceptPressed = () => {
dispatch({
type: 'acceptPressed',
payload: {
userInfo: true,
}
});
}
return (
<View style={styles.container}>
{showAlert && (
Alert.alert(
"Alert Title",
"My Alert Msg",
[
{
text: "Cancel",
onPress: handleCancelPressed,
},
{
text: "Ok",
onPress: handleAcceptPressed ,
},
],
{
cancelable: true,
}
);
)}
</View>
)}

useEffect is the way which can solve your problem for rendering it only once.
const [showAlert, setShowAlert] = useState(null);
useEffect(()=>{setShowAlert(true)},[])
The empty bracket denotes that, value for 'showAlert' will be set only once.
You should read useEffect in detail for implementing this properly.
I am not trying this example in any coding environment, I have only shared a proof of concept for you to work upon.
Hope this helps,
Regards,
Shameel Uddin

Related

React context isn't available from function inside component

Hello I have a problem with React Context. I'm using React-DnD and I'm trying to get context in feed function but it's empty. In this function I'm getting context default value but outside that function I'm getting correct values and I don't know why.
const Dog = () => {
const needsCtx = useContext(NeedsContext);
const invCtx = useContext(InventoryContext);
console.log(invCtx);
const feed = () => {
console.log(invCtx);
};
needsCtx.saveCurrentContextToDatabase();
const [{ isOver }, drop] = useDrop(
() => ({
accept: "food",
drop: () => feed(),
collect: (monitor) => ({
isOver: !!monitor.isOver(),
}),
}),
[]
);
return (
<div className={styles["wrapper-dog"]}>
<img ref={drop} className={styles.dog} alt="dog" src={dog}></img>
</div>
);
};
context:
import React, { useState } from "react";
export const InventoryContext = React.createContext({
items: {},
setItems: () => {},
});
const InventoryContextProvider = (props) => {
const [items, setItems] = useState({});
return (
<InventoryContext.Provider value={{ items: items, setItems: setItems }}>
{props.children}
</InventoryContext.Provider>
);
};
export default InventoryContextProvider;
I'm going to make suggestions here, and continue to edit this answer.
<InventoryContext.Provider value={{ items: items, setItems: setItems }}>
This line confuses me most. Mostly because, you're using state inside of the Provider, so why do you also need to declare items and setItems inside the context itself?
Since you're using the state inside the Provider, couldn't you remove the items and setItems inside the context itself, and then just pass as:
<InvetoryContext.Provider value={{ items, setItems }}>

How to fetch data from a custom React hook (API) with onClick and display it in a Div using TypeScript?

I'm new to TypeScript and even React Hooks. I'm trying to fetch data continuously from a custom hook called useFetch(), which takes in a string (the URL) parameter:
import { useEffect, useState } from "react";
export interface Payload {
data: null | string,
loading: boolean
}
const useFetch = (url: string) => {
const [state, setState] = useState<Payload>({data: null, loading: true})
useEffect(() => {
setState(state => ({ data: state.data, loading: true }));
fetch(url)
.then(x => x.text())
.then(y => {
setState({ data: y, loading: false });
});
}, [url, setState])
return state;
}
export default useFetch;
I import that hook into App():
import React, { useState, useEffect } from "react";
import useFetch from './utils/useFetch'
import {Payload} from './utils/useFetch';
const App = () => {
const [quote, setquote] = useState<Payload>({data: null, loading: true})
const handleClick = () => setquote(data)
const data = useFetch(
"/api/rand"
);
return (
<div>
<h1>Quotes</h1>
<button onClick={handleClick}>Get Quote</button>
<div>{quote.data}</div>
</div>
)
}
export default App;
On the first time the app loads, it works. I get data (a quote) when I click the button.
However, when I click it multiple times, the API isn't called again, and new data doesn't come through. I believe I'm supposed to use the useEffect() react hook and maintain the state (maybe I'm mistaken) but all my attempts so far have been to no avail. Any help would be much appreciated. If I figure it out, I'll definitely answer this question. Thanks!
It seems like you are mixing two ideas a little bit here.
Do you want to fetch data continuously? (Continuously meaning all the time, as in, periodically, at an interval)
Or do you want to fetch data upon mount of the component (currently happens) AND when the user taps the "Get Quote" button (not working currently)?
I will try to help you with both.
Continuously / periodically
Try changing the setup a bit. Inside the useFetch do something like:
import { useEffect, useState } from "react";
export interface Payload {
data: null | string,
loading: boolean
}
const useFetch = (url: string) => {
const [state, setState] = useState<Payload>({data: null, loading: true})
useEffect(() => {
const interval = setInterval(() => {
setState(state => ({ data: state.data, loading: true }));
fetch(url)
.then(x => x.text())
.then(y => {
setState({ data: y, loading: false });
});
}, 2000); // Something like 2s
return () => {
clearInterval(interval); // clear the interval when component unmounts
}
}, [url])
return state;
}
export default useFetch;
This will fetch the endpoint every 2s and update the state variable, this will then be reflected in the App (or any place that uses the hook for that matter).
Therefore you do not need the quote state anymore in the App. Also, you don't need a button in the UI anymore.
App will looks something like:
import React, { useState, useEffect } from "react";
import useFetch from './utils/useFetch'
import {Payload} from './utils/useFetch';
const App = () => {
const quote = useFetch(
"/api/rand"
);
return (
<div>
<h1>Quotes</h1>
<div>{quote.data}</div>
</div>
)
}
export default App;
Fetch data upon mount of the component (currently happens) AND when the user taps the "Get Quote" button
Then the useFetch hook isn't really needed/suited in my opinion. A hook can be used for this application, but transforming it into a more simple function would make more sense to me. I suggest omitting the useFetch hook. The App component would look something like:
import React, { useState, useEffect, useCallback } from "react";
const App = () => {
const [quote, setQuote] = useState<Payload>({data: null, loading: true})
const handleGetQuote = useCallback(() => {
setQuote(state => ({ data: state.data, loading: true }));
fetch("/api/rand")
.then(x => x.text())
.then(y => {
setQuote({ data: y, loading: false });
});
}, []);
useEffect(() => {
handleGetQuote();
}, [handleGetQuote]);
return (
<div>
<h1>Quotes</h1>
<button onClick={handleGetQuote}>Get Quote</button>
<div>{quote.data}</div>
</div>
)
}
export default App;
The problem here is, that you are not refetching the quotes.
const useFetch = (url:string) => {
const [loading, setLoading] = useState(false);
const [quote, setQuote] = useState('');
const refetch = useCallback(() => {
setLoading(true);
fetch(url)
.then((x) => x.text())
.then((y) => {
//setQuote(y);
setQuote(String(Math.random()));
setLoading(false);
});
}, [url]);
return { quote, refetch, loading };
};
const App = () => {
const { quote, refetch, loading } = useFetch('/api/rand');
useEffect(() => {
refetch();
}, []);
return (
<div>
<h1>Quotes</h1>
<button onClick={refetch}>Get Quote</button>
{loading ? ' loading...' : ''}
<div>{quote}</div>
</div>
);
};
See this example
https://stackblitz.com/edit/react-ts-kdyptk?file=index.tsx
Here's another possibility: I added a reload function to the return values of useFetch. Also, since the useFetch hook already contains its own state, I removed the useState from App
const useFetch = (url: string) => {
const [state, setState] = useState<Payload>({data: null, loading: true})
const [reloadFlag, setReloadFlag] = useState(0)
useEffect(() => {
if(reloadFlag !== 0){ // remove this if you want the hook to fetch data initially, not just after reload has been clicked
setState(state => ({ data: state.data, loading: true }));
fetch(url)
.then(x => x.text())
.then(y => {
setState({ data: y, loading: false });
});
}
}, [url, setState, reloadFlag])
return [state, ()=>setReloadFlag((curFlag)=>curFlag + 1)];
}
const App = () => {
const [data, reload] = useFetch(
"/api/rand"
);
return (
<div>
<h1>Quotes</h1>
<button onClick={reload}>Get Quote</button>
<div>{quote.data}</div>
</div>
)
}
Just change the code as below:
const useFetch = (url: string) => {
const [state, setState] = useState<Payload>({data: null, loading: true})
setState(state => ({ data: state.data, loading: true }));
fetch(url)
.then(x => x.text())
.then(y => {
setState({ data: y, loading: false });
});
return state;
}
export default useFetch;

Apply loading state to the items that are clicked in an array - React

I'm trying to set loader only for the item that is clicked. Currently, the problem I'm facing is, it applies loading state to all the items in the array.
I created a working example using CodeSandbox. Could anyone please help?
export default function App() {
const [state, dispatch] = useReducer(reducer, initialState);
const cars = [{ name: "Benz" }, { name: "Jeep" }, { name: "BMW" }];
const handleClick = () => {
// API Call
dispatch({ type: "SET_LOADING", loading: true });
// Loading stops once response is recieved.
};
return (
<div className="App">
{cars.map(({ name }) => (
<button onClick={handleClick}>
{state.loading ? "Loading..." : name}
</button>
))}
</div>
);
}
Instead of having a boolean ,you could use the name of the car for the loading property & then checking the value of loading property should suffice.I am assuming names are unique,otherwise add an id property & check with it.
import { useReducer } from "react";
import "./styles.css";
const initialState = {
loading: false
};
const reducer = (state, action) => {
switch (action.type) {
case "SET_LOADING":
return { ...state, loading: action.loading };
default:
return state;
}
};
export default function App() {
const [state, dispatch] = useReducer(reducer, initialState);
const cars = [{ name: "Benz" }, { name: "Jeep" }, { name: "BMW" }];
const handleClick = (e,name) => {
// API Call
dispatch({ type: "SET_LOADING", loading: name });
// Loading stops once response is recieved.
// Assing loading = '' once response is received
};
return (
<div className="App">
{cars.map(({ name }) => (
<button onClick={(e)=>{handleClick(e,name)}}>
{state.loading===name ? "Loading..." : name}
</button>
))}
</div>
);
}

Changing app navigation structure from version 4 to 5 in react native

I was working on an old app using react navigation version 4 the app contains a register and login in page obviously and then the content of the app.
recently I started remaking the content of the app using react navigation version 5 in order to use the shared element animation and the bottom tab navigator and it was fairly simple.
but I struggled with converting the login part to version 5 since the app structure is somewhat complicated and I am somewhat new to react navigation version 5.
i will leave a figure of the app structure bellow a long with samples of the code used.
App.js :
import { setNavigator } from "./app/navigationRef";
const articleListFlow = createStackNavigator({
Main: MainScreen, // screen with diffrent articles categories
ResultsShow: ResultShowScreen, // article details screen
});
const loginFlow = createStackNavigator({
Signup: SignupScreen,
Signin: SigninScreen,
});
loginFlow.navigationOptions = () => {
return {
headerShown: false,
};
};
articleListFlow.navigationOptions = {
title: "News Feed",
tabBarIcon: ({ tintColor }) => (
<View>
<Icon style={[{ color: tintColor }]} size={25} name={"ios-cart"} />
</View>
),
activeColor: "#ffffff",
inactiveColor: "#ebaabd",
barStyle: { backgroundColor: "#d13560" },
};
const switchNavigator = createSwitchNavigator({
ResolveAuth: ResolveAuthScreen,
MainloginFlow: createSwitchNavigator({
//WelcomeScreen: WeclomeScreen,
loginFlow: loginFlow,
}),
mainFlow: createMaterialBottomTabNavigator(
{
articleListFlow: articleListFlow,
ArticleSave: ArticleSaveScreen, // we dont need this one
Account: AccountScreen,
},
{
activeColor: "#ffffff",
inactiveColor: "#bda1f7",
barStyle: { backgroundColor: "#6948f4" },
}
),
});
const App = createAppContainer(switchNavigator);
export default () => {
return (
<AuthProvider>
<App
ref={(navigator) => {
setNavigator(navigator);
}}
/>
</AuthProvider>
);
};
NavigationRef.js :
import { NavigationActions } from "react-navigation";
let navigator;
export const setNavigator = (nav) => {
navigator = nav;
};
export const navigate = (routeName, params) => {
navigator.dispatch(
NavigationActions.navigate({
routeName,
params,
})
);
};
// routename is the name of the routes singin singup accountscreen
// params information we want to pass to the screen we want to show
AuthContext.js
import { AsyncStorage } from "react-native";
import createDataContext from "./createDataContext";
import userAPI from "../api/user";
// using navigate to access the navigator and redirect the user
import { navigate } from "../navigationRef";
// AUTHENTICATION REDUCERS
const authReducer = (state, action) => {
switch (action.type) {
case "add_error": {
return {
...state,
errorMessage: action.payload,
};
}
case "clear_error_message": {
return {
...state,
errorMessage: "",
};
}
case "signin": {
return {
errorMessage: "",
token: action.payload,
};
}
default:
return state;
}
};
// CLEARING ERROR MESSAGES WHEN SWITCHING SIGNIN-SIGNUP
const clearErrorMessage = (dispatch) => () => {
dispatch({ type: "clear_error_message" });
};
// AUTOMATIC SIGNIN ONLY USING TOKENS ON USER DEVICE
const tryLocalSignin = (dispatch) => async () => {
const token = await AsyncStorage.getItem("token");
if (token) {
// if token exists
dispatch({ type: "signin", payload: token });
navigate("Main");
} else {
// if token doesnt exist
navigate("WelcomeScreen");
}
};
// SIGNUP
const signup = (dispatch) => async ({ email, password }) => {
try {
const response = await userAPI.post("/signup", { email, password });
await AsyncStorage.setItem("token", response.data.token);
dispatch({ type: "signin", payload: response.data.token });
// making use of the navigate component to access navigation
// and redirect the user
navigate("Main");
} catch (err) {
dispatch({
type: "add_error",
payload: "Something went wrong with sign up",
});
}
};
// SIGNIN
const signin = (dispatch) => async ({ email, password }) => {
try {
const response = await userAPI.post("/signin", { email, password });
await AsyncStorage.setItem("token", response.data.token);
// using signin since the logic is the same
dispatch({ type: "signin", payload: response.data.token });
// making use of the navigate component to access navigation
// and redirect the user
navigate("Main");
} catch (err) {
console.log(err);
dispatch({
type: "add_error",
payload: "Something went wrong with sign in",
});
}
};
// SIGNOUT
const signout = (dispatch) => async () => {
// removing the token makes identification not work again
await AsyncStorage.removeItem("token");
dispatch({ type: "signout" });
navigate("loginFlow");
};
// CREATING CONTEXT AND PROVIDER OBJECTS FOR AUTHENTICATION
export const { Provider, Context } = createDataContext(
authReducer,
{
signin,
signup,
signout,
clearErrorMessage,
tryLocalSignin,
},
{
token: null,
errorMessage: "",
}
);
createDataContext.js
import React, { useReducer } from "react";
export default (reducer, actions, defaultValue) => {
const Context = React.createContext();
const Provider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, defaultValue);
const boundActions = {};
for (let action in actions) {
// for every action in the actions, call it with dispatch
boundActions[action] = actions[action](dispatch);
}
return (
<Context.Provider value={{ state, ...boundActions }}>
{children}
</Context.Provider>
);
};
return { Context, Provider };
};
My appologies for the long code and thank you in advance for anyone who can help.
There are several things that you need to consider when moving from V4 to V5 it involves some changes and also you can consider using features like the hooks.
The first change will be removing the Switch Navigator and conditionally render the navigator in its place. This will be done in your App.js. As you already have a reducer based implementation you can use the state values to take this decision.
The next change will be the creation of stacks, in V4 you create the navigation by passing the screen, now everything is a component and you pass the screens as children.
The option are also sent as props to either the navigator or the screen itself.
The usage of navigation ref is still possible but you can also use hooks like usenavigation inside components and for your authentication flow you wont be using this as you conditionally render the navigators.
I have made a simplified version based on your code.
App.js
const AuthStack = createStackNavigator();
const AppTabs = createMaterialBottomTabNavigator();
const ArticleStack = createStackNavigator();
const Articles = () => {
return (
<ArticleStack.Navigator>
<AppTabs.Screen name="ArticlesList" component={ArticleList} />
<AppTabs.Screen name="ArticlesDetails" component={ArticleDetail} />
</ArticleStack.Navigator>
);
};
export default function App() {
const [state, dispatch] = React.useReducer(authReducer, {
isLoading: true,
token: null,
errorMessage: '',
});
React.useEffect(() => {
const bootstrapAsync = async () => {
const userToken = await AsyncStorage.getItem('userToken');
dispatch({ type: 'RESTORE_TOKEN', token: userToken });
};
bootstrapAsync();
}, []);
const authContext = React.useMemo(
() => ({
signIn: async (data) => {
dispatch({ type: 'SIGN_IN', token: 'dummy-auth-token' });
},
signOut: () => dispatch({ type: 'SIGN_OUT' }),
signUp: async (data) => {
dispatch({ type: 'SIGN_IN', token: 'dummy-auth-token' });
},
}),
[]
);
return (
<AuthContext.Provider value={authContext}>
<NavigationContainer>
{state.token === null ? (
<AuthStack.Navigator headerMode="none">
{state.isLoading ? (
<AuthStack.Screen name="Welcome" component={WelcomeScreen} />
) : (
<>
<AuthStack.Screen name="SignIn" component={SignInScreen} />
<AuthStack.Screen name="SignUp" component={SingUpScreen} />
</>
)}
</AuthStack.Navigator>
) : (
<AppTabs.Navigator
activeColor="#f0edf6"
inactiveColor="#3e2465"
barStyle={{ backgroundColor: '#694fad' }}>
<AppTabs.Screen
name="Articles"
component={Articles}
options={{
tabBarLabel: 'Home',
tabBarIcon: ({ color, size }) => (
<MaterialCommunityIcons
name="home"
color={color}
size={size}
/>
),
}}
/>
<AppTabs.Screen name="Search" component={SearchScreen} />
<AppTabs.Screen name="Save" component={SaveScreen} />
<AppTabs.Screen name="Account" component={AccountScreen} />
</AppTabs.Navigator>
)}
</NavigationContainer>
</AuthContext.Provider>
);
}
Auth Context
const AuthContext = React.createContext();
export default AuthContext;
Auth Reducer
export const authReducer = (state, action) => {
switch (action.type) {
case 'RESTORE_TOKEN':
return {
...state,
token: action.token,
isLoading: false,
};
case 'SIGN_IN': {
return {
errorMessage: '',
token: action.payload,
};
}
case 'SIGN_OUT': {
return {
errorMessage: '',
token: null,
};
}
default:
return state;
}
};
As you can see the flow will be showing the welcome screen till the token is loaded from async storage and then based on that show the tabs or the login screen. Also the parameters are passed as props. I've moved the actions to app.js but it can be separated as well.
You can see a fully running sample here
https://snack.expo.io/#guruparan/navigation-sample-3
Hope this helps, Feel free to ask if there are any questions.
As per your diagram, I have tried to create Navigation
const WelcomeStack = createStackNavigator();
const Tab = createBottomTabNavigator();
const ArticleStack = createStackNavigator();
const MainStack = createStackNavigator();
function Welcome(){
return(
<WelcomeStack.Navigator>
<WelcomeStack.screen name='SignIn' component={SignIn}/>
<WelcomeStack.screen name='SignUp' component={SignUp}/>
</WelcomeStack.Navigator>
)
}
function Article(){
return(
<ArticleStack.Navigator>
<ArticleStack.Screen name='ArtcileList' name={ArticleList}/>
<ArticleStack.Screen name='ArticleDetail' name={ArtcileDetail}/>
</ArticleStack.Navigator>
)
}
function TabNav(){
<Tab.Navigator>
<Tab.Screen name='Article' component={Article}/>
<Tab.Screen name='Search' component={Search}/>
<Tab.Screen name='Save' component={Save}/>
<Tab.Screen name='Account' component={Account}/>
</Tab.Navigator>
}
function App(){
return(
<NavigationContainer>
<MainStack.Navigator>
{this.state.isLogin ?
<MainStack.Screen name='Tab' component={TabNav}/>
:
<MainStack.Screen name = 'WelcomeStack' component={Welcome}/>
}
</MainStack.Navigator>
</NavigationContainer>
)
}
In react navigation 5, their is no switch navigator so you have to go with stack navigation + ternary operator.
This is just an idea as per your diagram. You can make it better after some R&D.

Initial default state is not showing, displaying empty

This is from a tutorial assignment from Dave Ceddia's Redux course, I am trying to display the initial state, which contains an array of objects, however it is simply returning undefined and not displaying anything. I am new to React, and I have hit a wall on getting 1) my buttons to display the state, and 2) default state to appear initially.
I have tried to have my component Buttons as a class, and constant.
I have tried stating my initialReducer in the default: return state; in my reducer as well. I have also tried different syntax for my dispatch actions, but nothing seems to be getting to the reducer.
index.js
import React, { Fragment } from "react";
import ReactDOM from "react-dom";
import { getAllItems, addEventToBeginning, addEventToEnd } from "./actions";
import { connect, Provider } from "react-redux";
import { store } from "./reducers";
const Buttons = ({
state,
getAllItems,
addEventToBeginning,
addEventToEnd
}) => (
<React.Fragment>
<ul>{state ? state.actions.map(item => <li>{item}</li>) : []}</ul>
<button onClick={getAllItems}> Display items </button>
<button onClick={addEventToBeginning}> addEventToBeginning </button>
<button onClick={addEventToEnd}> addEventToEnd </button>
</React.Fragment>
);
const mapDispatchToProps = { getAllItems, addEventToBeginning, addEventToEnd };
const mapStateToProps = state => ({
actions: state.actions,
sum: state.sum
});
connect(
mapStateToProps,
mapDispatchToProps
)(Buttons);
reducers.js
const initialState = {
actions: [
{ id: 0, type: "SALE", value: 3.99 },
{ id: 1, type: "REFUND", value: -1.99 },
{ id: 2, type: "SALE", value: 17.49 }
],
sum: 0
};
const newUnit = { id: Math.random * 10, type: "SALE", value: Math.random * 25 };
function eventReducer(state = initialState, action) {
switch (action.type) {
case ADD_EVENT_TO_BEGINNING:
const copy = { ...state };
copy.actions.unshift(newUnit);
return copy;
case ADD_EVENT_TO_END:
const copy2 = { ...state };
copy2.actions.unshift(newUnit);
return copy2;
cut out for cleanliness
case GET_ITEMS:
return {
...state,
actions: state.actions,
sum: state.sum
};
default:
return state;
}
}
export const store = createStore(eventReducer);
example of actions.js (they all follow same format)
export const ADD_EVENT_TO_BEGINNING = "ADD_EVENT_TO_BEGINNING";
export function addEventToBeginning() {
return dispatch => {
dispatch({
type: ADD_EVENT_TO_BEGINNING
});
};
}
UPDATE:
Thank you #ravibagul91 and #Yurui_Zhang, I cut everything but getAllItems out, and changed the state to:
const initialState = {
itemsById: [
{ id: 0, type: "SALE", value: 3.99 },
{ id: 1, type: "REFUND", value: -1.99 },
{ id: 2, type: "SALE", value: 17.49 }
]
};
class Form extends React.Component {
render() {
return (
<div>
{this.props.itemsById
? this.props.itemsById.map(item => (
<li>
{item.id} {item.type} {item.value}
</li>
))
: []}
<button onClick={this.getAllItems}> Display items </button>
</div>
);
}
}
const mapDispatchToProps = { getAllItems };
function mapStateToProps(state) {
return {
itemsById: state.itemsById
};
}
export function getAllItems() {
return dispatch => ({
type: "GET_ITEMS"
});
}
There are multiple problems with your code:
const mapStateToProps = state => ({
actions: state.actions,
sum: state.sum
});
Here you have mapped redux state fields to props actions and sum - your component won't receive a state prop, instead it will receive actions and sum directly.
so your component really should be:
const Button = ({
actions,
sum,
}) => (
<>
<ul>{actions && actions.map(item => <li>{item}</li>)}</ul>
</>
);
your mapDispatchToProps function is not defined correctly. It should be something like this:
// ideally you don't want the function names in your component to be the same as the ones you imported so I'm renaming it here:
import { getAllItems as getAllItemsAction } from "./actions";
// you need to actually `dispatch` the action
const mapDispatchToProps = (dispatch) => ({
getAllItems: () => dispatch(getAllItemsAction()),
});
Your reducer doesn't seem to be defined correctly as well, however you should try to fix the problems I mentioned above first :)
Try not to do too much in one go when you are learning react/redux. I'd recommend reviewing the basics (how the data flow works, how to map state from the redux store to your component, what is an action-creator, etc.).
As you are destructuring the props,
const Buttons = ({
state,
getAllItems,
addEventToBeginning,
addEventToEnd
}) => ( ...
You don't have access to state, instead you need to directly use actions and sum like,
const Buttons = ({
actions, // get the actions directly
sum, // get the sum directly
getAllItems,
addEventToBeginning,
addEventToEnd
}) => (
<React.Fragment>
//You cannot print object directly, need to print some values like item.type / item.value
<ul>{actions && actions.length && actions.map(item => <li>{item.type} {item.value}</li>)}</ul>
<button onClick={getAllItems}> Display items </button>
<button onClick={addEventToBeginning}> addEventToBeginning </button>
<button onClick={addEventToEnd}> addEventToEnd </button>
</React.Fragment>
);
Your mapDispatchToProps should be,
const mapDispatchToProps = dispatch => {
return {
// dispatching actions returned by action creators
getAllItems : () => dispatch(getAllItems()),
addEventToBeginning : () => dispatch(addEventToBeginning()),
addEventToEnd : () => dispatch(addEventToEnd())
}
}
Or you can make use of bindActionCreators,
import { bindActionCreators } from 'redux'
function mapDispatchToProps(dispatch) {
return {
dispatch,
...bindActionCreators({ getAllItems, addEventToBeginning, addEventToEnd }, dispatch)
}
}
In reducer, ADD_EVENT_TO_END should add element to end of the array, but you are adding again at the beginning using unshift. You should use push which will add element at the end of array,
case ADD_EVENT_TO_END:
const copy2 = { ...state };
copy2.actions.push(newUnit); //Add element at the end
return copy2;
Also your GET_ITEMS should be as simple as,
case GET_ITEMS:
return state;

Categories

Resources