i have this slice of code:
const ChangeRequest = ({
inRelease,
title,
requestId,
cost,
supervisor,
chipText,
description,
showModal,
}) => (
<div
onClick={() => showModal({
modal: 'changeRequestDetail',
state: { title },
})}
className={styles.crContainer}
>
Main problem
in the const ChangeRequest i've the value title, i need to pass this value to the onClick property of the modal.
modal
<div
onClick={() => showModal({
modal: 'changeRequestDetail',
state: { title },
})}
className={styles.crContainer}
>
In this way if i try to console.log(title) i get undefined as value.
Question
how can i pass the value of title to the value state of show modal in the onClick function ?
Added the showModal
so i called it with mapDispatchToProps
const mapDispatchToProps = {
showModal: showModalAction,
};
this is the relative action
export const SHOW_MODAL = 'SHOW_MODAL';
export const HIDE_MODAL = 'HIDE_MODAL';
export function showModalAction(params) {
return dispatch => dispatch({
type: SHOW_MODAL,
payload: params,
});
}
export function hideModalAction(params) {
return dispatch => dispatch({ type: HIDE_MODAL });
}
this is his reducer:
import { Map } from 'immutable';
import { SHOW_MODAL, HIDE_MODAL } from '../actions/modalActions';
const initialState = Map({
type: null,
state: null,
});
const modalReducer = (state = initialState, action) => {
switch (action.type) {
case SHOW_MODAL:
return state
.set('type', action.payload.modal)
.set('state', action.payload.state);
case HIDE_MODAL:
return state
.set('type', null)
.set('state', null);
default:
return state;
}
};
export default modalReducer;
Yesterday, after several tries i succeed to got the Data.
What was the problem ?
we need to mapStateToProps the data from the state of the modal. Why ? because the modal was a reducer.
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import styles from './styles.module.css';
import BlueLightRow from './BlueLightRow';
import DetailSection from './DetailSection';
import Modal from '../../../components/Modal';
import Header from './Header';
function ChangeDetail({
title, supervisor, architect, initiator, id, cost, description, chipText,
}) {
return (
<Modal noPadding bigDialog>
{console.log(title)}
<div className={styles.blueBackground}>
<div className={styles.container}>
<Header title={title} chipText={chipText} />
<BlueLightRow cost={cost} id={id} />
<DetailSection supervisor={supervisor} architect={architect} claimant={initiator} description={description} />
</div>
</div>
</Modal>
);
}
ChangeDetail.propTypes = {
title: PropTypes.string.isRequired,
architect: PropTypes.string.isRequired,
initiator: PropTypes.string.isRequired,
supervisor: PropTypes.string.isRequired,
id: PropTypes.string.isRequired,
cost: PropTypes.number.isRequired,
description: PropTypes.string.isRequired,
chipText: PropTypes.string.isRequired,
};
const mapStateToProps = state => ({
title: state.getIn(['modalReducer', 'state', 'title']),
architect: state.getIn(['modalReducer', 'state', 'architect']),
initiator: state.getIn(['modalReducer', 'state', 'initiator']),
supervisor: state.getIn(['modalReducer', 'state', 'supervisor']),
description: state.getIn(['modalReducer', 'state', 'description']),
cost: state.getIn(['modalReducer', 'state', 'cost']),
id: state.getIn(['modalReducer', 'state', 'requestId']),
chipText: state.getIn(['modalReducer', 'state', 'chipText']),
});
export default connect(mapStateToProps, null)(ChangeDetail);
If someone will need more explanation about the code i will be happy to go deep if i can :)
Related
I have a problem that i'm sure isn't that incredibly complicated but i'm lost on it. I have looked at the other stack answers and I'm still just as lost hoping someone a fresh set of eyes from someone who knows react can give me a hand. What i'm trying to do is return a modal on click. The issue i'm facing is the props being undefined and when I click I get nothing.
const Collection = ({
isLoading,
cues = [],
id,
loadCollection,
onSave,
onRemove,
savedCount,
}) => {
useEffect(() => loadCollection(id), [loadCollection, id]);
const [collection, setCollection] = useState({});
const isMounted = useIsMounted();
const getCollectionData = useCallback(() => {
client.get(`collections/${id}`).then(r => {
if (isMounted.current) setCollection(r.data);
});
}, [id, isMounted, setCollection]);
useEffect(getCollectionData, [id, savedCount]);
return collection.id ? (
<CueList
isLoading={isLoading}
cues={cues}
defaultProps = {cues}
title={collection.name}
description={collection.description}
image={collection.image_src}
isSaved={collection.is_saved}
onSave={() => onSave(id)}
onRemove={() => onRemove(id)}
withVersionsDropdown
buttons={[
{
title: 'Share Playlist', //issue is right in here
Icon: props => (
<Share {...props} width={15} height={15} noMargin />
),
onClick: () =>
this.props.onSharePlaylistModal(this.props.playlist),
},
]}
/>
) : (
<Loading />
);
};
Collection.propTypes = {
cues: PropTypes.array,
loadCollection: PropTypes.func.isRequired,
onSave: PropTypes.func.isRequired,
onRemove: PropTypes.func.isRequired,
isLoading: PropTypes.bool.isRequired,
id: PropTypes.number.isRequired,
onSharePlaylistModal: PropTypes.func.isRequired,
savedCount: PropTypes.number.isRequired,
};
const mapStateToProps = ({ pageDisplay, cues, sidebar }, { match }) => {
const id = parseInt(match.params.id);
return {
savedCount: sidebar.savedCollections.length,
isLoading: pageDisplay.isLoading,
cues: getCues({
...cues,
sortBy: pageDisplay.sortBy,
sortInBackEnd: false,
}),
id,
};
};
const mapDispatchToProps = (
dispatch,
) => ({
loadCollection: id => {
dispatch(actions.LOAD_COLLECTION(id));
},
onSave: id => {
dispatch(actions.SAVE_COLLECTION(id));
},
onRemove: id => {
dispatch(actions.REMOVE_COLLECTION(id));
},
openSharePlaylistModal: data => {
dispatch(actions.OPEN_MODAL(actions.SHARE_PLAYLIST(undefined, data)));
},
});
export default connect(
mapStateToProps,
mapDispatchToProps,
)(Collection);
This part seems problematic
{
title: 'Share Playlist',
Icon: props => (
<Share {...props} width={15} height={15} noMargin />
),
onClick: () =>
this.props.onSharePlaylistModal(this.props.playlist),
}
Currently you probably have an error in the console when you click on the button
Since you are in a functional component, you are not expected to use the keyword this
The props onSharePlaylistModal and playlist should be accessible from the argument
const Collection = ({
isLoading,
cues = [],
id,
loadCollection,
onSave,
onRemove,
savedCount,
onSharePlaylistModal, // <= here
playlist
}) => {
...
As a side note, it feels like you are looking at old and deprecated react/redux tutorial and mixing things up.
connect is rather old fashioned and it is probably preferable to use hooks
this.props is used in class component and not functional component
Hi I'm new to REACT and I have a HW where I need to create a grocery shopping list and I need to create a clear button. The isPurchased key value pair is a boolean though. I need to create a button that when I click Purchased it clears that grocery item off my list. Any help would be appreciated.
class App extends Component {
state = {
grocery: grocery,
item: '',
brand: '',
units: Number,
quantity: Number,
isPurchased: Boolean
}
handleChange = (e) => {
this.setState({ [e.target.id]: e.target.value })
}
handleSubmit = (e) => {
e.preventDefault()
const addGrocery = {
item: this.state.item,
brand: this.state.brand,
units: this.state.units,
quantity: this.state.quantity,
}
this.setState({
grocery: [addGrocery, ...this.state.grocery],
item: '',
brand: '',
units: Number,
quantity: Number,
})
const removeGrocery = {
item: this.state.item
}
}
hey here is a full code for creating a to do list in react (it will be very similar to your problem):
**
Summary
** of the idea of creating a to-do list or shopping list is that each to-do will be an object, when we create a new object we will insert it into an array. once it is in the array by using the array.map() function we will convert each object to an HTML element to make the UI.
if something is unclear I am here to answer
file - App.js:
import React, { useState, useReducer } from "react";
import Todo from "./Todo";
export const ACTIONS = {
ADD_TODO: "add-todo",
TOGGLE_TODO: "toggle-todo",
DELETE_TODO: "delete-todo",
};
function reducer(todos, action) {
switch (action.type) {
case ACTIONS.ADD_TODO:
return [...todos, newTodo(action.payload.name)];
case ACTIONS.TOGGLE_TODO:
return todos.map((todo) => {
if (todo.id === action.payload.id) {
return { ...todo, complete: !todo.complete }; //change to complete if we found to id that toggled
}
return todo;
});
case ACTIONS.DELETE_TODO:
return todos.filter((todo) => todo.id !== action.payload.id);
default:
return todos;
}
}
function newTodo(name) {
return { id: Date.now(), name: name, complete: false };
}
const App = () => {
const [todos, dispatch] = useReducer(reducer, []); //useReducer return the state and the reducer function
const [name, setName] = useState("");
function handleSubmit(e) {
e.preventDefault();
dispatch({ type: ACTIONS.ADD_TODO, payload: { name: name } });
setName("");
}
return (
<>
<form onSubmit={handleSubmit}>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</form>
{todos.map((todo) => {
return <Todo key={todo.id} todo={todo} dispatch={dispatch} />;
})}
</>
);
};
export default App;
Another file (component) - Todo.js:
import React from "react";
import { ACTIONS } from "./App";
const Todo = ({ todo, dispatch }) => {
return (
<div>
<span style={{ color: todo.complete ? "#AAA" : "#000" }}>
{todo.name}
</span>
<button
onClick={() =>
dispatch({ type: ACTIONS.TOGGLE_TODO, payload: { id: todo.id } })
}
>
Toggle
</button>
<button
onClick={() =>
dispatch({ type: ACTIONS.DELETE_TODO, payload: { id: todo.id } })
}
>
Delete
</button>
</div>
);
};
export default Todo;
In this problem what can I do so that the on clicking the button, both the function add to the cart and select the checkbox executes together? In the current scenario add to cart is working when the button is clicked but the checkbox isn't selected. I removed all the styles so that the actual code is readable
YourItems.js
import React from "react";
import { connect } from "react-redux";
import { addOn } from "./data";
import { addOnhandleChange,addOnSelector} from "./AddOnActions";
const YourItems = ({ addOnhandleChange, addOnSelector, selectedId }) => {
return (
<div>
{addOn.map(({ id, name, img, price, unit }) => {
return (
<div key={id}>
<div>
<img src={img} alt={name} />
<p>{name}</p>
<span>Rs. {price}</span>
<input
type="checkbox"
checked={id === selectedId}
onChange={() => addOnhandleChange(id)}
/>
</div>
<button onClick={() =>addOnSelector({id, name,img,price,unit, })}>
Add
</button>
</div>
)})}
</div>
);
};
const mapStateToProps = (state) => {
return {
selectedId: state.addOn.selectedId,
};
};
export default connect(mapStateToProps, { addOnSelector,addOnhandleChange})(YourItems);
AddOnAction.js
export const addOnhandleChange = (id) => (dispatch) => {
dispatch({
type: "SELECTED_ID",
payload: id,
});
};
export const addOnSelector = ({ id, name, img, price, unit }) => (dispatch) => {
dispatch({
type: "ADD_TO_CART",
payload: { id, name, img, price, unit },
});
};
reducer.js
const initialState = {
selectedId: "",
};
export default function SelectorReducer(
state = initialState,
action
) {
switch (action.type) {
case "SELECTED_ID":
return {
...state,
selectedId: action.payload,
};
default:
return state;
}
}
data.js
export const addOn = [
{
id: 12654,
img: "https://images.pexels.com/photos/1132047/pexels-photo-1132047.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
name: "Banana",
price: 10,
unit: 1,
},
{
id: 2256435,
img: "https://images.pexels.com/photos/1132047/pexels-photo-1132047.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
name: "Mango",
price: 20,
unit: 1,
},
{
id: 3429684,
img: "https://images.pexels.com/photos/1132047/pexels-photo-1132047.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
name: "Grape",
price: 30,
unit: 1,
},
];
Add a case for "ADD_TO_CART" action type and use the id packed in action.payload in the reducer.
export default function SelectorReducer(
state = initialState,
action
) {
switch (action.type) {
case "SELECTED_ID":
return {
...state,
selectedId: action.payload,
};
case "ADD_TO_CART":
return {
...state,
selectedId: action.payload.id, // <-- use the payload.id
};
default:
return state;
}
}
I want to integrate react-md with redux, but I don't understand how to trigger the onAutocomplete function. For now I only want to get some hard coded data from the Action, later on I'll add an api call and the search text as parameter.
Here is my action with the hard coded data that I want to dispatch:
export const searchCityAutoComplete = () => {
// no need for text parameter to search at this point
const users = [{
id: '1',
name: 'Robin',
}, {
id: '2',
name: 'Yan',
}]
return {
type: "AUTOCOMPLETE_SEARCH",
payload: users
};
}
Here is the reducer:
const initState = {
searchResults: [],
}
const sitesReducer = (state = initState, action) => {
switch (action.type) {
case "AUTOCOMPLETE_SEARCH":
state = {
...state,
searchResults: action.payload
}
break;
default:
return state;
}
return state;
}
export default sitesReducer;
And here is the component
import React from 'react';
import { connect } from 'react-redux';
import { searchCityAutoComplete } from '../actions/sitesActions';
import Autocomplete from 'react-md/lib/Autocompletes';
const SearchAutocomplete = ({ searchResults, onAutocomplete }) => (
<div >
<div className="md-text-container" style={{ marginTop: "10em" }}>
<Autocomplete
id="test-autocomplete"
label="Autocomplete"
dataLabel="name"
autocompleteWithLabel
placeholder="Search Users"
data={searchResults}
onAutocomplete={(...args) => {
searchCityAutoComplete(args)
console.log(args);
}}
deleteKeys={[
"id",
]}
simplifiedMenu={false}
anchor={{
x: Autocomplete.HorizontalAnchors.CENTER,
y: Autocomplete.VerticalAnchors.TOP
}}
position={Autocomplete.Positions.BOTTOM}
/>
</div>
</div>
);
const mapStateToProps = state => {
console.log(state)
return {
searchResults: state.sitesReducer.searchResults,
}
}
const mapDispatchToProps = dispatch => ({
onAutocomplete: () => { dispatch(searchCityAutoComplete()) }
})
export default connect(mapStateToProps, mapDispatchToProps)(SearchAutocomplete);
As you probably notice, the onAutocomplete function isn't in the same scope as the component... so I guess that's why it's not triggered. For a starting point I just need to get the data from the action - once I type in the autocomplete text box...thanks.
From react-md docs :
onAutocomplete : An optional function to call when an autocomplete suggestion is clicked either by using the mouse, the enter/space key,
or touch.
And so onAutocomplete is only called when you select a suggestion. And it's not what you're looking for. What you're looking for is the onChange prop :
onChange : An optional function to call when the Autocomplete's text field value changes.
Here you can find a simple example code : https://codesandbox.io/s/muddy-cdn-l85sp
You can just pass your onAutocomplete action straight into Autocomplete component:
const SearchAutocomplete = ({ searchResults, onAutocomplete }) => (
<div>
<div className="md-text-container" style={{ marginTop: "10em" }}>
<Autocomplete
id="test-autocomplete"
label="Autocomplete"
dataLabel="name"
autocompleteWithLabel
placeholder="Search Users"
data={searchResults}
onAutocomplete={onAutocomplete} // Pass the action from props here
deleteKeys={[
"id",
]}
simplifiedMenu={false}
anchor={{
x: Autocomplete.HorizontalAnchors.CENTER,
y: Autocomplete.VerticalAnchors.TOP
}}
position={Autocomplete.Positions.BOTTOM}
/>
</div>
</div>
);
Then in mapDispatchToProps you'll need to accept autocomplete value and do a search on it or set it to reducer:
const mapDispatchToProps = dispatch => ({
onAutocomplete: (value) => dispatch(searchCityAutoComplete(value))
})
export const searchCityAutoComplete = (value) => {
// do smth with the value
const users = [{
id: '1',
name: 'Robin',
}, {
id: '2',
name: 'Yan',
}]
return {
type: "AUTOCOMPLETE_SEARCH",
payload: users
};
}
i wanted to store the Email in the redux store and i am unable to do so here is my sign in component and redux store any help would be appreciated i am using react-navigation
My Dispatch Method is invoked on the initial load as well as on every key stroke for email input i want that to invoke only on hit of continue button
I need a way to store the email in the store and retrieve it in some other screen later
SignUp.js
import React, { Component } from 'react';
import {
StyleSheet,
View,
KeyboardAvoidingView,
Keyboard,
TouchableWithoutFeedback,
Alert
} from 'react-native';
import { SocialIcon } from 'react-native-elements';
import PropTypes from 'prop-types';
import { Header } from 'react-navigation';
import { connect } from 'react-redux';
import {
Container, Footer, FooterContainer, DefaultInput, Typography
} from '../components/common';
import { validate } from '../config';
import * as actionTypes from '../store/actions';
const styles = StyleSheet.create({
container: {
flex: 1
},
input: {
width: '80%',
height: 40
}
});
class SignUp extends Component {
state = {
controls: {
email: {
value: '',
valid: false,
validationRules: {
isEmail: true
},
touched: false
},
password: {
value: '',
valid: false,
validationRules: {
minLength: 6
},
touched: false
},
confirmPassword: {
value: '',
valid: false,
validationRules: {
equalTo: 'password'
},
touched: false
}
}
};
updateInputState = (key, value) => {
let connectedValue = {};
const stateObject = this.state;
if (stateObject.controls[key].validationRules.equalTo) {
const equalControl = stateObject.controls[key].validationRules.equalTo;
const equalValue = stateObject.controls[equalControl].value;
connectedValue = {
...connectedValue,
equalTo: equalValue
};
}
if (key === 'password') {
connectedValue = {
...connectedValue,
equalTo: value
};
}
this.setState(prevState => ({
controls: {
...prevState.controls,
confirmPassword: {
...prevState.controls.confirmPassword,
valid:
key === 'password'
? validate(
prevState.controls.confirmPassword.value,
prevState.controls.confirmPassword.validationRules,
connectedValue
)
: prevState.controls.confirmPassword.valid
},
[key]: {
...prevState.controls[key],
value,
valid: validate(value, prevState.controls[key].validationRules, connectedValue),
touched: true
}
}
}));
};
render () {
const stateData = this.state;
const { navigation } = this.props;
return (
<KeyboardAvoidingView
style={styles.container}
behavior="padding"
keyboardVerticalOffset={Header.HEIGHT + 20}
>
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<Container>
<Typography textType="loginLabelStyle" textLabel="Use any of your existing profiles" />
<View style={{
flexDirection: 'row',
justifyContent: 'space-between',
}}
>
<SocialIcon type="twitter" />
<SocialIcon type="facebook" />
<SocialIcon type="google" light onPress={this.signIn} />
</View>
<Typography textType="loginLabelStyle" textLabel="or create one on SimpliFid" />
<DefaultInput
placeholder="Your E-Mail Address"
style={styles.input}
value={stateData.controls.email.value}
onChangeText={val => this.updateInputState('email', val)}
valid={stateData.controls.email.valid}
touched={stateData.controls.email.touched}
autoCapitalize="none"
autoCorrect={false}
keyboardType="email-address"
/>
<DefaultInput
placeholder="Password"
style={styles.input}
value={stateData.controls.password.value}
onChangeText={val => this.updateInputState('password', val)}
valid={stateData.controls.password.valid}
touched={stateData.controls.password.touched}
secureTextEntry
/>
<DefaultInput
placeholder="Confirm Password"
style={styles.input}
value={stateData.controls.confirmPassword.value}
onChangeText={val => this.updateInputState('confirmPassword', val)}
valid={stateData.controls.confirmPassword.valid}
touched={stateData.controls.confirmPassword.touched}
secureTextEntry
/>
<FooterContainer>
<Footer
leftButtonHandler={() => navigation.goBack()}
rightButtonHandler={this.props.onSignUp(stateData.controls.email.value, navigation)}
/* rightButtonHandler={() => navigation.navigate('ChatBot')} */
/>
</FooterContainer>
</Container>
</TouchableWithoutFeedback>
</KeyboardAvoidingView>
);
}
}
SignUp.propTypes = {
navigation: PropTypes.shape({
navigate: PropTypes.func.isRequired
}).isRequired
};
const mapDispatchToProps = dispatch => ({
onSignUp: (email, navigation) => {
Alert.alert(email);
dispatch({ type: actionTypes.SIGNUP, email });
navigation.navigate('ChatBot');
}
});
export default connect(
null,
mapDispatchToProps
)(SignUp);
Reducers.js
import * as actionTypes from './actions';
const initialState = {
email: '',
accountType: '',
name: '',
dob: '',
address: '',
ssn: '',
phoneNumber: '',
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case actionTypes.SIGNUP:
return {
...state,
email: action.email,
};
default:
return state;
}
};
export default reducer;
You are calling the this.props.onSingUp methods on each render
Try wrapping the call in a handler method:
handleRightButton = () => {
this.props.onSignUp(this.state..controls.email.value, this.props.navigation);
}
// And on render
render() {
...
rightButtonHandler={this.handleRightButton}
...
}
The problem was that i was trying to access the store in a wrong way i was trying using this
import state from '../store/reducers';
const Email = state.email;
However the correct way and probably the only way to access the store is using mapStateToProps
const mapStateToProps = state => ({
email: state.email,
});
<Footer
leftButtonHandler={() => navigation.goBack()}
rightButtonHandler={(event) => {
event.preventDefault();
this.props.onSignUp(stateData.controls.email.value,navigation)
/>
Try adding the event.preventDefault() in the rightButtonHandler.