Redux mapDispatchToProps access action within array.map - javascript

I am trying to get an action to be available within a function that is called within array.map. Passing the props from parent component to subcomponent is happening. It feels like it should be a simple task to make it available within the array.map function, but that has not proven successful.
Here's an updated example code:
In this example, being able to properly pass the 'actionUpdate' function to listItem is what I have not accomplished successfully.
function listItem(item,index) {
const id = item.id
const handleUpdated = e => {
e.preventDefault();
actionUpdate(id);
};
return (
<Button
onClick={handleUpdated}
color='primary'
variant='contained'
style={{ marginTop: '0.75rem' }}
>
Update
</Button>
);
}
function MyList(props) {
const { streams } = props;
return (
<List>
{streams.map(listItem)};
</List>
);
}
function listView(props) {
const { classes, streams } = props;
return (
<div style={{ width: '100%' }}>
<section className={classes.content}>
<Grid container>
<Grid item style={{ width: '100%' }}>
<MyList
streams={streams}
actionUpdate={actionUpdate}
/>
</Grid>
</Grid>
</section>
</div>
);
}
const mapStateToProps = state => {
const streams = R.path(['theObject', 'arrayOfObjects'])
)(state);
return { streams };
};
const mapDispatchToProps = dispatch => {
const actionUpdate = (itemId) => {
return dispatch(theUpdateAction(itemId));
};
return { actionUpdate };
};
const MainListView = connect(
mapStateToProps,
mapDispatchToProps
)(listView);
export default withStyles(styles)(MainListView);
I have my state and actions mapped to props using connect, mapStateToProps and mapDispatchToProps.
What I need to achieve is having access to the action from the dispatch within the listItem function.

You could define listItem within MyList, which would provide it access to MyList's props including updateAction.
function MyList(props) {
const { streams, actionUpdate } = props;
// Can access updateAction here
const listItem = (item,index) => {
const id = item.id
const handleUpdated = e => {
e.preventDefault();
actionUpdate(id);
};
return (
<Button
onClick={handleUpdated}
color='primary'
variant='contained'
style={{ marginTop: '0.75rem' }}
>
Update
</Button>
);
}
return (
<List>
{streams.map(listItem)};
</List>
);
}
function listView(props) {
const { classes, streams } = props;
return (
<div style={{ width: '100%' }}>
<section className={classes.content}>
<Grid container>
<Grid item style={{ width: '100%' }}>
<MyList
streams={streams}
actionUpdate={actionUpdate}
/>
</Grid>
</Grid>
</section>
</div>
);
}
const mapStateToProps = state => {
const streams = R.path(['theObject', 'arrayOfObjects'])
)(state);
return { streams };
};
const mapDispatchToProps = dispatch => {
const actionUpdate = (itemId) => {
return dispatch(theUpdateAction(itemId));
};
return { actionUpdate };
};
const MainListView = connect(
mapStateToProps,
mapDispatchToProps
)(listView);
export default withStyles(styles)(MainListView);

Related

child component pass props to parent's useState -> returning undefined (react)

I want to put the props from the child component into the value of useState from the parent component, but it also comes with undefined. How can I not get undefined?
Child component
const FilterSelect = props => {
const [filterUser, setFilterUser] = useState('all')
const handleFilterUser = ({ target }) => {
setFilterUser(target.value)
}
useEffect(() => {
props.propFunction(filterUser)
}, [filterUser])
return (
<Box sx={{ width: 120 }}>
<FormControl fullWidth>
<Select
size="small"
labelId="user-select-label"
id="user-select"
value={filterUser}
label="filter"
onChange={handleFilterUser}
>
</Select>
</FormControl>
</Box>
)
}
parent component
import FilterSelect from './components/FilterSelect'
const UserList = () => {
const [filterSelectOne, setFilterSelectOne] = useState('all')
const highFunction = text => {
setFilterSelectOne(text)
}
useEffect(() => {
highFunction()
}, [])
console.log(filterSelectOne) // 'all', undefined
return (
<Box sx={{ width: '100%' }}>
<Paper sx={{ width: '100%', mb: 2 }}>
<FilterSelect propFunction={highFunction} />
</Paper>
</Box>
)
}
Remove the use effect where you call the highFunction with no parameters in the parent component

Why am i getting undefined in my console, when i try to subscribe and use my context using typescript and react

When trying to create a simple quiz app without the need to prop drilling I've stumbled upon an issue while trying to integrate context into the project. The issue is that when subscribing to my context as shown below and console. logging 'name', I get the value of undefined. What am I missing in order to get my name(stored in a state in my context) logged instead of getting undefined?
My context
import React, { createContext, Dispatch, SetStateAction, useContext, useState } from 'react';
export interface IUserContextType {
name: string;
test: string;
setName: Dispatch<SetStateAction<string>>;
setTest: Dispatch<SetStateAction<string>>;
}
type IQuizContextProvidorProps = {
children: React.ReactNode;
};
export const QuizContext = createContext({} as IUserContextType);
export const useQuizContext = () => useContext(QuizContext);
const QuizContexProvider = ({ children }: IQuizContextProvidorProps) => {
const [name, setName] = useState('Marvin');
const [test, setTest] = useState('This is a test');
const values = { name, test, setName, setTest };
return <QuizContext.Provider value={values}>{children}</QuizContext.Provider>;
};
export default QuizContexProvider;
My App
import { useState } from 'react';
import './App.css';
import quizApi from './utils/quiz.json';
import { IQuiz, IQuizAnswers } from './model/IQuiz';
import { Button, LinearProgress, Paper, styled, Typography } from '#mui/material';
import { Box } from '#mui/system';
import QuizContexProvider, { useQuizContext } from './utils/QuizContex';
const QuizContainer = styled(Box)(({ theme }) => ({
'.correct': {
backgroundColor: 'darkseagreen',
},
'.linearProgress': {
height: '1rem',
},
}));
function App() {
const { name, test } = useQuizContext();
console.log('name', name);
function shuffle(array: Array<any>) {
return array.sort(() => Math.random() - 0.5);
}
const quiz: Array<IQuiz> = shuffle(quizApi);
const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0);
const [progress, setProgress] = useState(0);
const [viewQuiz, setViewQuiz] = useState(true);
const [quizScore, setQuizScore] = useState(0);
const inkrementWith = 100 / quiz.length;
const handleProgress = () => {
setProgress(progress + inkrementWith);
};
const handleAnswer = (answers: IQuizAnswers) => {
const nextQuestion = currentQuestionIndex + 1;
handleProgress();
if (nextQuestion < quiz.length) {
setCurrentQuestionIndex(nextQuestion);
} else {
setViewQuiz(false);
}
if (answers.isTrue === true) {
setQuizScore(quizScore + 1);
}
};
const handleReset = () => {
setCurrentQuestionIndex(0);
setProgress(0);
setQuizScore(0);
setViewQuiz(true);
};
return (
<QuizContexProvider>
<QuizContainer className='App'>
<Box component='header' className='App-header'>
{viewQuiz ? (
<>
<Box sx={{ width: '50%' }}>
<LinearProgress className='linearProgress' variant='determinate' color='success' value={progress} />
</Box>
{quiz.map(
(question, index) =>
index === currentQuestionIndex && (
<Box key={index}>
<Box>{question.questionLabel}</Box>
<Box sx={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem', margin: '1rem' }}>
{shuffle(question.answerOptions).map((answers, index) => (
<Paper
key={index}
onClick={() => {
return handleAnswer(answers);
}}
component='button'
>
{answers.answerLabel}
</Paper>
))}
</Box>
</Box>
)
)}
</>
) : (
<Paper>
<Typography component='h1' variant='h3'>
Quiz results
</Typography>
<Typography component='h2' variant='subtitle1'>
Quiz results
</Typography>
<Typography component='h1' variant='h1' sx={{ fontWeight: 700 }}>
{quizScore} / {quiz.length}
</Typography>
<Button variant='contained' onClick={handleReset} sx={{ margin: '1rem 0rem' }}>
Reset quiz
</Button>
</Paper>
)}
</Box>
</QuizContainer>
</QuizContexProvider>
);
}
export default App;
Any component that wish to use context value, should be wrapped inside the provider. Your <App /> component is using context value, so it should be:
<QuizContexProvider>
<App />
</QuizContexProvider>
You can put the provider in Index.ts file.

How Can I target single item by map button in React Typescript?

So I have a functional components here:
export default function Test() {
const [products, setProduct] = useState<any>([]);
const [image, setImage] = useState<any>([""]);
const [prices, setPrice] = useState<any>([]);
const [showPrice, setShowPrice] = useState<boolean>(false);
const [PhonePrice, setPhonePrice] = useState<any>("");
useEffect(() => {
async function loadProducts() {
const res = await fetch("http://raw.githubusercontent.com/reborn094/Practice/main/data.json", {
method: "GET",
});
const json = await res.json();
const data = json.products;
setProduct(data);
return (
<div>
{products.map((product: any, index: number) => {
return (
<Col>
<Card key={index} style={{ width: "20rem" }}>
<Card.Body>
<Card.Title>
</Card.Title>
<Card.Title>
<Child ProductImage={image[index]} name={product.name} code={product.code} onSelectProduct={()=>{
>
what should I write here????
------------------------
}}
></Child>
</Card.Title>
<Card.Text></Card.Text>
</Card.Body>
</Card>
</Col>
);
})}
</div>
);
}
And here is my Child components :
export default function Child(props:{
onSelectProduct?:()=> void;
}) {
return (
<div>
<Button onClick={props.onSelectProduct}></Button>
</div>
)
}
My question is What if I want to set button in Test components to target single item in list, what should I do? Because Now If I set Button that Button would trigger all item.What should I do in the function onSelectProduct?

dispatch is not updating reducer

I'm using useReducer and useContext as a flux store and i'm boggled on why i'm unable to update the stores state. Here is a sample of the code I'm working with.
reducer and initial state
export const localState = {
modal_open: false,
date: null
}
export const reducer = (state , action) => {
switch (action.type) {
case "OPEN_MODAL":
return {
...state,
modal_open: true
}
case "CLOSE_MODAL":
return {
...state,
modal_open: false
}
case "SET_DATE":
return {
...state,
date: action.payload
}
default:
return state;
}
};
context constructor and component wrapping
import React from "react";
const LocalWebSiteContext= React.createContext();
const WebsiteDetails = () => {
const [state, dispatch] = React.useReducer(reducer, localState)
const [averPositions, setAverPositions] = React.useState()
const classes = useStyles();
let match = useRouteMatch("/app/websites/:id/:slug");
let { id } = useParams();
const context = useContext(UserContext);
const history = useHistory();
//testing
const { localContext } = context
const websiteData = localContext.websites.find((el, idx) => {
if(el.website_id === id){
return el;
}
});
const userAndWebsiteData = {
...websiteData,
...localContext.user
}
if(!userAndWebsiteData){
console.log('Not a valid website');
history.push('/app/websites/');
// return;
}
return (
<>
<localWebSiteContext.Provider value={{state, dispatch}}>
{ userAndWebsiteData &&
<Page className={classes.root} title="Analytics Dashboard">
{/* { JSON.stringify(userAndWebsiteData) }
{ id } */}
<Header websiteData={userAndWebsiteData} />
<Grid className={classes.container} container spacing={3}>
<Grid item xs={12}>
<Overview websiteData={userAndWebsiteData} />
</Grid>
<Grid item lg={8} xl={9} xs={12}>
<CustomModal />
<FinancialStats websiteData={userAndWebsiteData}/>
</Grid>
<Grid item lg={4} xl={3} xs={12}>
{/* {
JSON.stringify(userAndWebsiteData.positionRangesData)}
*/}
<Top5To30 positionData={userAndWebsiteData.positionRangesData
? userAndWebsiteData.positionRangesData : {} } />
range bar chart
<EarningsSegmentation />
</Grid>
<Grid item lg={12} xs={12}>
<KeywordsList />
</Grid>
<Grid item lg={4} xs={12}>
<CustomerActivity />
</Grid>
</Grid>
</Page>
}
</localWebSiteContext.Provider>
</>
);
};
Component where i'm dispatching the payload to change date
const FinancialStats = props => {
const {state, dispatch} = React.useContext(localWebSiteContext)
const { websiteData, className, handleAverData, ...rest } = props;
const [dateId, setDateId] = React.useState(null)
const [disabled, setDisabled] = useState([]);
const handleModalOpen = () => {
const averPositions = websiteData.dailyAveragePositions;
if (averPositions){
const dateNum = parseInt(averPositions[0].d)
dispatch({type: 'OPEN_MODAL', payload: dateNum})
}
}
return (
<span
key={dataKey}
className="legend-item"
style={style}
>
<Surface width={10} height={10}>
<Symbols cx={5} cy={5} type="circle" size={50} fill={color} />
{active && (
<Symbols
cx={5}
cy={5}
type="circle"
size={25}
fill={'#FFF'}
/>
)}
</Surface>
<span>{dataKey}</span>
</span>
);
})}
</div>
);
};
The expected behavior is when I dispatch({type: 'SET_DATE', payload: dateNum) it will reflect that all other components subscribed to the useContext hook. But i cannot for the life of me get it to update from null... I'm able to switch the bool values. Even when I tried to set it locally in a useState hook it's still null? Maybe has something to do with renders? Thanks in advance!!

pass multiple refs to child components

Before diving to the main problem, my use case is I am trying to handle the scroll to a desired section. I will have navigations on the left and list of form sections relative to those navigation on the right. The Navigation and Form Section are the child component. Here is how I have structured my code
Parent.js
const scrollToRef = ref => window.scrollTo(0, ref.current.offsetTop);
const Profile = () => {
const socialRef = React.useRef(null);
const smsRef = React.useRef(null);
const handleScroll = ref => {
console.log("scrollRef", ref);
scrollToRef(ref);
};
return (
<>
<Wrapper>
<Grid>
<Row>
<Col xs={12} md={3} sm={12}>
<Navigation
socialRef={socialRef}
smsRef={smsRef}
handleScroll={handleScroll}
/>
</Col>
<Col xs={12} md={9} sm={12}>
<Form
socialRef={socialRef}
smsRef={smsRef}
/>
</Col>
</Row>
</Grid>
</Wrapper>
</>
);
};
Navigation.js(child component)
I tried using forwardRef but seems like it only accepts one argument as ref though I have multiple refs.
const Navigation = React.forwardRef(({ handleScroll }, ref) => {
// it only accepts on ref argument
const items = [
{ id: 1, name: "Social connections", pointer: "social-connections", to: ref }, // socialRef
{ id: 2, name: "SMS preference", pointer: "sms", to: ref }, // smsRef
];
return (
<>
<Box>
<UL>
{items.map(item => {
return (
<LI
key={item.id}
active={item.active}
onClick={() => handleScroll(item.to)}
>
{item.name}
</LI>
);
})}
</UL>
</Box>
</>
);
});
export default Navigation;
Form.js
I do not have idea on passing multiple refs when using forwardRef so for form section I have passed the refs as simple props passing.
const Form = ({ socialRef, smsRef }) => {
return (
<>
<Formik initialValues={initialValues()}>
{({ handleSubmit }) => {
return (
<form onSubmit={handleSubmit}>
<Social socialRef={socialRef} />
<SMS smsRef={smsRef} />
</form>
);
}}
</Formik>
</>
);
};
Social.js
const Social = ({ socialRef }) => {
return (
<>
<Row ref={socialRef}>
<Col xs={12} md={3}>
<Label>Social connections</Label>
</Col>
<Col xs={12} md={6}></Col>
</Row>
</>
);
};
Can anyone help me at passing multiple refs so when clicked on the particular navigation item, it should scroll me to its respective component(section).
I have added an example below. I have not tested this. This is just the idea.
import React, { createContext, useState, useContext, useRef, useEffect } from 'react'
export const RefContext = createContext({});
export const RefContextProvider = ({ children }) => {
const [refs, setRefs] = useState({});
return <RefContext.Provider value={{ refs, setRefs }}>
{children}
</RefContext.Provider>;
};
const Profile = ({ children }) => {
// ---------------- Here you can access refs set in the Navigation
const { refs } = useContext(RefContext);
console.log(refs.socialRef, refs.smsRef);
return <>
{children}
</>;
};
const Navigation = () => {
const socialRef = useRef(null);
const smsRef = useRef(null);
const { setRefs } = useContext(RefContext);
// --------------- Here you add the refs to context
useEffect(() => {
if (socialRef && smsRef) {
setRefs({ socialRef, smsRef });
}
}, [socialRef, smsRef, setRefs]);
return <>
<div ref={socialRef}></div>
<div ref={smsRef}></div>
</>
};
export const Example = () => {
return (
<RefContextProvider>
<Profile>
<Navigation />
</Profile>
</RefContextProvider>
);
};

Categories

Resources