I get id is not defined in fetchEvents, when trying to use button.value as a parameter.
I am using mapDispatchToProps and mapStateToProps in my component.
const mapDispatchToProps = (dispatch) => {
return {
resetForm: () => dispatch(resetForm()),
fetchEvents: setSubCategory => dispatch(fetchEvents(id))
};
};
const mapStateToProps = state => {
return {
setCredentials: state.setCredentials,
categories: state.fetchCategories,
isLoading: state.isLoading
};
};
I then destructure my props to get my id:
const {
fetchEvents,
resetForm,
isLoading,
setCredentials: { setStudent, setGroup, setYear }
} = this.props;
const id = setStudent || setGroup || setYear;
However, when I dispatch my action:
const buttonOptions = [
{
key: 0,
label: "refresh",
value: Id,
icon: "undo"
},
{
key: 1,
label: "back",
value: Id,
icon: "caret-left"
}
];
return (
<View style={styles.container}>
{buttonOptions.map((button, i) => {
const style =
i == 0 ? styles.divContainerLeft : styles.divContainerRight;
return (
<View style={style} key={"view" + i}>
<TouchableOpacity
disabled={isLoading ? true : false}
key={"TouchableOpacity" + i}
// dispatch action here
onPress={i == 0 ? () => fetchEvents(button.value) : resetForm}
>
<Icon
name={button.icon}
style={styles.button}
color="white"
key={"icon" + i}
size={30}
/>
</TouchableOpacity>
</View>
);
})}
</View>
);
In your mapDispatchToProps, you are re-declaring the parameter name to setSubCategory, yet, in your fetch call you pass id.
Instead, try this:
const mapDispatchToProps = (dispatch) => {
return {
resetForm: () => dispatch(resetForm()),
fetchEvents: (setSubCategory) => dispatch(fetchEvents(setSubCategory))
};
};
OR
const mapDispatchToProps = (dispatch) => {
return {
resetForm: () => dispatch(resetForm()),
fetchEvents: (id) => dispatch(fetchEvents(id))
};
};
This is because you are declaring and inlining fetchEvents as an anonymous function. The parameter names must match.
Related
In React-Native I´ve two TextInputs and an add-button
My add function works i.e it creates a list. But each time I fill out the form, it adds a new listpost. I want to check if action.payload.number exist, and if true, increase state.points with action.payload.points.
as yet everything I tried with have failed i.e it didn't recognize state.number === action.payload.number and adds a new row in the list
Please help me solving this issue
Thanks in advance
Pierre
const InputScreen = () => {
const [number, setNumber] = useState("");
const [points, setPoints] = useState("");
const {addScorer} = useContext(Context);
const onPress = () => {
addScorer(number, points);
setnumber("");
setPoints("");
};
return (
<View>
<Text style={styles.label}>enter Scorer Number:</Text>
<TextInput style={styles.input} value={number} onChangeText={setNumber} />
<Text style={styles.label}>enter Points Scored:</Text>
<TextInput style={styles.input} value={points} onChangeText={setPoints} />
<TouchableOpacity onPress={onPress}>
<FontAwesome5 name="plus-circle" size={44} color="coral" />
</TouchableOpacity>
</View>
);
};
export default InputScreen;
const scorerReducer = (state, action) => {
const id = Date.now().toString().slice(-4);
switch (action.type) {
case "add_scorer":
// if (state.number === action.payload.nummer) {
if (state.find((scorer) => scorer.id === action.payload.id)) {
return [
...state,
{
points: state.points + +action.payload.points,
},
];
} else {
return [
...state,
{
id: id,
number: action.payload.number,
points: +action.payload.points,
},
];
}
default:
return state;
}
};
const addScorer = (dispatch) => {
return (number, points, id) => {
dispatch({type: "add_scorer", payload: {number, points, id}});
};
};
export const {Context, Provider} = createDataContext(
scorerReducer,
{addScorer},
[]
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
I solved it without using Reducer:)
export const ScoredProvider = ({children}) => {
const [playerList, setPlayerList] = useState([]);
const [number, setNumber] = useState("");
const [points, setPoints] = useState("");
const addScorer = () => {
const players = [...playerList];
if (number.trim().length === 0) {
return;
}
const posit = players.map((player) => player.number).indexOf(number);
if (posit !== -1) {
setPlayerList((playerList) =>
playerList.map((scorer, index) =>
index === posit
? {
...scorer,
points: scorer.points + +points,
}
: scorer
)
);
} else {
const newScorer = {
id: Date.now(),
number: number,
points: +points,
};
setPlayerList([...playerList, newScorer]);
setPoints(points);
}
};
return (
<ScoredContext.Provider
value={{number, setNumber, points, setPoints, playerList, addScorer}}
>
{children}
</ScoredContext.Provider>
);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Im working on simply COVID-19 tracker and i have a problem.
Is there any option in Apollo for React to fetch graphql data once per button press?
Now i have TextInput and Button but when i fetch data once i can't type another country in input because i have immediately error.
const Tile = () => {
const [country, setCountry] = useState('Poland');
const [cases, setCases] = useState(0);
const MY_QUERY = gql`
query getCountryStats($country: String!) {
country(name: $country) {
todayCases
}
}
`;
const [getCountryStats, {data, loading, error}] = useLazyQuery(MY_QUERY, {
variables: {
country: country,
},
onCompleted: (data) => {
setCases(data.country.todayCases);
},
});
if (loading) return <Text>LOADING...</Text>;
if (error) return <Text>Error!</Text>;
return (
<View>
<CasesNumber>{cases}</CasesNumber>
<FinderWrapper>
<FinderInput
onChangeText={(text) => {
setCountry(text);
}}
/>
<FinderButton
onPress={() => {
getCountryStats();
}}>
<Text>FIND</Text>
</FinderButton>
</FinderWrapper>
</View>
);
};
export default Tile;
Try using this
const Tile = () => {
const [country, setCountry] = useState('Poland');
const [cases, setCases] = useState(0);
let inputValue = ‘’;
const MY_QUERY = gql`
query getCountryStats($country: String!) {
country(name: $country) {
todayCases
}
}
`;
const [getCountryStats, {data, loading, error}] = useLazyQuery(MY_QUERY, {
variables: {
country: country,
},
onCompleted: (data) => {
setCases(data.country.todayCases);
},
});
const onGetCountryStats = () => {
setCountry(inputValue);
getCountryStats();
}
if (loading) return <Text>LOADING...</Text>;
if (error) return <Text>Error!</Text>;
return (
<View>
<CasesNumber>{cases}</CasesNumber>
<FinderWrapper>
<FinderInput
onChangeText={(text) => {
inputValue = text;
}}
/>
<FinderButton
onPress={() => {
onGetCountryStats();
}}>
<Text>FIND</Text>
</FinderButton>
</FinderWrapper>
</View>
);
};
export default Tile;
I am trying to save groupChatName as the value of the TextInput and save that name on the backend but in doing so I am getting the error "Possible unhandled promise, data: null, variable 'groupChatName' has coerced Null value for NonNull type 'String'", path: null.
export const createGroupChat = `mutation createGroupChat($groupChatName:String! $messages:String $createdUser:String! $users:String) {
createGroupChat(input:{
groupChatName:$groupChatName
messages:$messages
createdUser:$createdUser
users:$users
}){
groupChatName
messages
createdUser
users
}
}`;
const [currentUser, setCurrentUser] = useState('');
const [value, setValue] = useState('');
const [groupChatName, setGroupChatName] = useState('');
useEffect(() => {
Auth.currentAuthenticatedUser()
.then(currentUser => setCurrentUser(currentUser))
.catch (() => setUsername(null));
}, []);
GroupChat = () => {
if (value.length > 0) {
setValue('')
setGroupChatName('')
props.navigation.navigate('Message', { value });
}
};
GroupChatMutation = async () => {
const GroupChatDetails = { groupChatName, currentUser };
const newGroupChat = await API.graphql(graphqlOperation(createGroupChat, { GroupChatDetails }));
console.log(JSON.stringify(newGroupChat));
};
return (
<View style={styles.container}>
<Text style={styles.header}>Create GroupChat Name</Text>
<View style={styles.textInputContainer}>
<TextInput
style={styles.textInput}
multiline={true}
placeholder={'Type in a GroupChatName'}
placeholderTextColor="#abbabb"
value={value}
onChangeText={value => setValue(value)}
onChange={value => setGroupChatName(value)}
/>
.......```
I need to set the height of the ownProps from within the component that has the ownProps. It doesn't seem to update, it is always undefined.
Can it be done?
Here is my code, with the line of code to look at being commented with //////////////////
const mapStateToProps = (state: State, ownProps): Object => ({
searchText: state.product.search.query.locationAutocomplete.searchText,
place: state.product.search.query.locationAutocomplete.place,
searchResults: state.product.search.query.locationAutocomplete.searchResults,
shouldHideResults:
state.product.search.query.locationAutocomplete.shouldHideResults,
height: ownProps.height,
onContentSizeChange: ownProps.onContentSizeChange
})
const mapDispatchToProps = (dispatch: Dispatch<*>): Object => ({
updateSearchQueryPageLocationAutocompleteSearchText: (searchText: string) => {
dispatch(
updateSearchQueryPageLocationAutocompleteSearchText({
searchText: searchText
})
)
},
updateSearchQueryPageLocationAutocompletePlace: (locationPlace: Place) => {
dispatch(
updateSearchQueryPageLocationAutocompletePlace({
locationPlace: locationPlace
})
)
},
getSearchQueryPageLocationAutocompletePlaceDetails: (placeId: number) => {
dispatch(
getSearchQueryPageLocationAutocompletePlaceDetails({ placeId: placeId })
)
},
getSearchQueryPageLocationAutocompleteResults: (text: string) => {
dispatch(getSearchQueryPageLocationAutocompleteResults({ text: text }))
},
updateProductSearchQueryPageIsLoctionListDisplayed: (displayed: boolean) => {
dispatch(updateProductSearchQueryPageIsLoctionListDisplayed(displayed))
},
updateLocationShouldHideResults: (shouldHideResults: boolean) => {
dispatch(updateSearchQueryPageShouldHideLocationResults(shouldHideResults))
}
})
let LocationView = (props: LocationViewProps): Object => {
return (
<Autocomplete
value={props.searchText}
customStyle={autocompleteStyle.customStyle(props.place)}
placeholder={'Location'}
updateValue={props.updateSearchQueryPageLocationAutocompleteSearchText}
updateDataBehindValue={
props.updateSearchQueryPageLocationAutocompletePlace
}
getDataBehindValueDetails={
props.getSearchQueryPageLocationAutocompletePlaceDetails
}
autocompleteResults={props.searchResults}
getSearchQueryPageLocationAutocompleteResults={
props.getSearchQueryPageLocationAutocompleteResults
}
shouldHideResults={props.shouldHideResults}
onListItemSelect={location => {
props.getSearchQueryPageLocationAutocompletePlaceDetails(
location.place_id
)
props.updateSearchQueryPageLocationAutocompleteSearchText(
location.description
)
props.updateLocationShouldHideResults(true)
props.updateProductSearchQueryPageIsLoctionListDisplayed(false)
}}
onContentSizeChange={height => {
console.log(height)
props.height = getHeight(height)}////////////////////////this line///////////////////////
}
onChangeText={text => onChangeText(props, text)}
/>
)
}
const getHeight = height => {
const h = Math.max(60, height)
return h
}
LocationView = connect(
mapStateToProps,
mapDispatchToProps
)(LocationView)
height: ownProps.height,
It's unseless, either you receive your props from the parent <MyComponent height={...} ..., either you pass the props from your store mapStateToProps = (state, ownProps) => ({ height: state..., //here ownProps contains to the props passed to MyComponent, so height={...} })
If you want to change the props passed from the parent, use a handle function like that
handleSetheight = height => this.setState({ height })
return (<Child height={this.state.height} setHeight={this.handleSetHeight}/>)
When swiping the row in the SwipeableListView I want to delete the rowitem and re-render the list.
What is now happening is that always the last item in the list is removed, not the item that is swiped.
Any ideas what is wrong?
export default class SwipeList extends Component {
constructor(props) {
super(props);
let ds = SwipeableListView.getNewDataSource();
this.favourites = []
this.state = {
ds:[],
dataSource:ds,
isLoading:true,
closeRow:false,
};
}
componentWillMount () {
store.get('KEY_FAV').then(value => {
typeof(value) === 'object'
? this.favourites = Object.keys(mockdata.favourite)
: this.favourites = JSON.parse(value)
this.setState({
dataSource: this.state.dataSource.cloneWithRowsAndSections(this.genData(this.favourites
)),
isLoading:false
})
})
}
genData = (list) => {
let dataBlob = []
for(let i = 0; i <list.length; i++) {
dataBlob.push({id:list[i], name:list[list[i]]})
}
return [dataBlob, []]
}
Till here it is okay, the SwipeableList is loaded with all RowItems.
But in the below handleSwipeAction() while setting new state for dataSource, the list will only delete the last item, not the selected.
handleSwipeAction = (rowData, SectionID, rowID) => {
AlertIOS.alert('Remove ' + rowData.name + ' \nfrom Favourites?', null,
[
{text:'Cancel', onPress: () => {this.setState({closeRow:true})}, style:'cancel'},
{text:'OK', onPress: () => {
this.favourites.slice()
this.favourites.splice(rowID, 1)
this.setState({
closeRow:true,
})
this.setState({//I THINK HERE IS THE PROBLEM
dataSource:this.state.dataSource.cloneWithRowsAndSections(this.genData(this.favourites))
})
store.set('KEY_FAV', this.favourites)
}}
])
}
onSwipe = (rowData, SectionID, rowID) => {
return (
<View style={styles.actionsContainer}>
<TouchableHighlight
onPress={() => this.handleSwipeAction(rowData, SectionID, rowID)}>
<Text style={styles.actionsItem}>Remove</Text>
</TouchableHighlight>
</View>
);
};
and the render function
render() {
if(this.state.isLoading) return null
return (
<View style={styles.container}>
<SwipeableListView
bounceFirstRowOnMount
enableEmptySections={true}
dataSource={this.state.dataSource}
maxSwipeDistance={this.props.swipeDistance}
renderRow={(item) => this.renderItem(item)}
renderQuickActions={this.onSwipe}
renderSeparator={this.renderSeperator}
doCloseRow={this.state.closeRow}
/>
</View>
);
}
when you are done slicing, I believe if you do:
let ds = SwipeableListView.getNewDataSource(); all over again, and then
this.setState({ dataSource: ds.cloneWithRowsAndSections(this.genData(this.favourites)) })
It should work. For a reason that I still don't get. Also I don't know why you do two setState() in your function. One is enough no?
So this should work:
handleSwipeAction = (rowData, SectionID, rowID) => {
AlertIOS.alert('Remove ' + rowData.name + ' \nfrom Favourites?', null,
[
{text:'Cancel', onPress: () => {this.setState({closeRow:true})}, style:'cancel'},
{text:'OK', onPress: () => {
this.favourites.slice()
this.favourites.splice(rowID, 1)
let ds = SwipeableListView.getNewDataSource(); // add this
this.setState({ dataSource: ds.cloneWithRowsAndSections(this.genData(this.favourites)), closeRow:true })
store.set('KEY_FAV', this.favourites) }} ])
}