In my axis, I am trying to dispatch a React event with a payload. When I console.log('item'), I get a pointer event logged. The dispatch has as payload: isTrusted (from the pointer event) instead of having the item property dispatched. Is this the correct way to trigger a dispatch event with D3.js?
axisGroup
.selectAll('.tick')
.data<BaseItemI>(itemsInDomain)
.style('cursor', 'pointer')
.on('click', function (item) {
console.log('clicked', item);
// dispatch.arguments = { type: SET_SELECTED_ITEM, payload: item };
dispatch({
type: SET_SELECTED_ITEM,
payload: { item: item, baseItem: undefined }
});
});
I tried using the dispatch from D3 but it is confusing. Also tried using a callback function instead of an anonymous function and I get the same behaviour.
.on('click', (item) => {
console.log('clicked', item);
dispatch({
type: SET_SELECTED_ITEM,
payload: { item: item, baseItem: undefined }
});
});
I did not manage to get the .on('click', () => {}) to work but I instead added an onClick to the g element that contains the axisRef.
<g
ref={ref}
style={{ cursor: 'pointer' }}
onClick={() => {
itemsInDomain.map((i) => {
dispatch({
type: SET_SELECTED_ITEM,
payload: { item: i, baseItem: undefined }
});
});
}}
></g>
Related
I am trying to write a react code to submit the value to the backend server.
I want the input field to be cleared out as soon as the user hits submit button.
I have written the below code, could anyone help me with what I am missing here?
class Create extends Component {
state = {
task : {
title: '',
completed: false
}
}
CreateHandler = (event) => {
this.setState((state) => {
return {
task: {
...state, title: '' // <----- CLEARING HERE (well, trying)
}
}
});
event.target.value=""; // <----- ALSO HERE
event.preventDefault();
axios({
method:'post',
url:'http://localhost:8000/api/task-create',
data: this.state.task,
xsrfHeaderName: this.props.CSRFToken
})
.then((res) => {
console.log(res.data);
})
this.props.updateState(this.state.task)
}
ChangeHandler = (event) => {
this.setState(state => {
return {
task: {
...state, title: event.target.value
}
}
})
}
Breaking the code in parts so that it's easily readable.
render() {
return (
<form onSubmit={this.CreateHandler.bind(this)}>
<div className="header form-group">
<input
className="newItem form-control"
onChange={this.ChangeHandler.bind(this)}
value={this.state.task.title}
/>
<button
type="submit"
class="saveButton btn btn-primary btn-warning">
submit
</button>
</div>
</form>
)
}
}
export default Create;
The end goal is to clear the input field and then send the data to the backend django server, which is being done successfully except the input field being cleared.
You are not updating state correctly
this.setState((state) => {
return {
task: {
...state, title: '' // <----- CLEARING HERE (well, trying)
}
}
});
should be
this.setState((state) =>({...state, task: {...state.task, title: ''}}))
In your case, it could be done like this:
this.setState(previousState => ({
task: {
...previousState.task,
title: '' // <----- CLEARING HERE
}
}));
A better way to write your createHandler method:
CreateHandler = (event) => {
// Prevent the default form action
event.preventDefault();
// Call your API
axios({
method: "post",
url: "http://localhost:8000/api/task-create",
data: this.state.task,
xsrfHeaderName: this.props.CSRFToken,
}).then((res) => {
// Request passed
// Call your prop function
this.props.updateState(this.state.task);
// Clear the unnecessary data
this.setState((prevState) => ({
// Create new object
task: {
// Assign the properties of previous task object
...prevState.task,
// Clear the title field
title: "",
},
}));
});
};
Hope this helps!
Im using vuex and I have an action
storeExpense(context, params){
axios.post('api/expenses', params)
.then( response => {
console.log("Expense Created");
})
.catch( error => {
console.log(error);
});
}
and on my Expense.vue im using the action via
this.$store.dispatch('storeExpense',this.expense)
.then( response => {
this.modalShow = false
this.$swal(
'Success',
'Expense has been created!',
'success'
)
})
I dont have an error but after the expense was created the state is not updating therefore I need to refresh the page in order for my table to get the latest data.
I have a mutation called
mutateExpenses(state, payload){
state.expenses = payload
}
however when i use this after the response it overrides the whole state.expenses object to a single object because this.expense is a single object
Im new to vuex.
You must update your store using mutations that are called inside your actions.
I suggest you to dive a bit into the Vuex documentation, especially the mutations and actions :)
Here is an example of how to use the store :
It goes dispatch --> action --> mutation
// Your store
const store = new Vuex.Store({
state: {
posts: [],
isLoading: false
},
mutations: {
// Must be called by actions AND ONLY by actions
add(state, post) {
// Add the given post to the 'posts' array in our state
Vue.set(state.posts, state.posts.length, post)
},
busy(state) {
Vue.set(state, 'isLoading', true)
},
free(state) {
Vue.set(state, 'isLoading', false)
}
},
actions: {
create({
commit
}, post) {
commit('busy')
axios.post('https://jsonplaceholder.typicode.com/posts', post)
.then(response => {
// Call the mutation method 'add' to add the newly created post
commit('add', response.data)
})
.catch((reason) => {
// Handle errors
})
.finally(() => {
commit('free')
});
},
}
})
// Your Vue app
new Vue({
el: "#app",
store,
data: {
post: {
title: 'foo',
body: 'bar',
userId: 1
}
},
methods: {
onButtonClicked() {
this.$store.dispatch('create', this.post)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.1.0/vuex.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<div id="app">
<button #click="onButtonClicked">Create Post</button>
<div>Posts : <span v-if="$store.state.isLoading">Requesting</span></div>
<div v-for="post in $store.state.posts">
{{post}}
</div>
</div>
I make a component, which show information from database in table. But this information with filters.
Filtering can be by event type and by participant (id: integer type).
When I click the button, I call handleShowClick(). In this function I check: if value of type event isn't null, I get from database events with this type. if value of type event is null, I get all events.
After this I check a participant value. If value isn't null, I call function, which search which events are include this participant. Data from this.state.event show in table in another component.
I haven't problems with event type. But I have problem with participant. When I choose one of participant, table shows correct data for a split second. After this return to prev state (without filter by participants).
How can I fix this issue? I set state to event only in this component
class TestPage extends Component {
constructor(props) {
super(props);
this.state = {
event: [],
searchByType: null,
searchByParticipant: null,
participantToEvent: []
};
this.handleShowClick = this.handleShowClick.bind(this);
this.onHandleEventByTypeFetch = this.onHandleEventByTypeFetch.bind(this);
this.handleParticipantSearch = this.handleParticipantSearch.bind(this);
this.onHandleEventFetch = this.onHandleEventFetch.bind(this);
}
handleShowClick() { // onClick
if (this.state.searchByType !== null) {
this.onHandleEventByTypeFetch(); // select * from ... where type=...
} else {
this.onHandleEventFetch(); // select * from ...
}
if (this.state.searchByParticipant !== null) {
this.handleParticipantSearch();
}
}
handleParticipantSearch() {
const list = [];
this.state.participantToEvent.map(itemP => { // participantToEvent is binding table
if (itemP.parid === this.state.searchByParticipant) {
this.state.event.map(itemEvent => {
if (itemEvent.id === itemP.eventid) {
list.push(itemEvent);
}
});
}
});
console.log(list); // here I see array with correct result
this.setState({ event: list });
}
onHandleEventFetch() {
fetch( ... , {
method: 'GET'
})
.then((response) => {
if (response.status >= 400) {
throw new Error('Bad response from server');
}
return response.json();
})
.then(data => {
if (data.length === 0) {
alert('nothing');
} else {
this.setState({
event: data
});
}
});
}
onHandleEventByTypeFetch() {
fetch( ... , {
method: 'GET'
})
.then((response) => {
if (response.status >= 400) {
throw new Error('Bad response from server');
}
return response.json();
})
.then(data => {
if (data.length === 0) {
alert('nothing');
} else {
this.setState({
event: data
});
}
});
...
}
}
Structure of this.state.event:
[{id: 1, name: 'New event', participant: 5, type: 10}, ...]
Structure of this.state.participantToEvent:
[{id: 1, idparticipant: 5, idevent: 1}, ...]
this.setState(...this.state,{ event: list });
I think this would solve your problem. Because you clear every item except for {event:list} by not copying the previous state.
Edit:
You should put
...this.state
to onHandleEventByeTypeFetch and onHandleEventFetch. Without them when you click handleShowClick one of those two functions always work and clears searchByParticipant data from the state by not copying the previous state.
The reason for you see the correct data for a short time is all about async nature of the state.
I'm a facing a bit of a problem here. I'm trying to pass a parameter from a function to this.setState callback, but I can't figure out how is this possible.
My code looks like this:
selectHandler(event){
this.setState({
selectedImage: event.target
}, (event) => {
this.markSelectedHandler(event)
})
}
markSelectedHandler(e){
e.target.classList.add('active')
if(e.target !== this.state.selectedImage && this.state.selectedImage){
this.state.selectedImage.classList.remove('active')
e.target.classList.add('active')
}
}
e.target returns null, any idea why this happens ?
The event will not work async. You will need to extract the values or use e.persist() reactjs.org/docs/events.html#event-pooling
You could however say:
selectHandler(event){
const { target } = event;
this.setState({
selectedImage: target
}, () => {
this.markSelectedHandler(target)
})
}
markSelectedHandler(target){
target.classList.add('active')
if(target!== this.state.selectedImage && this.state.selectedImage){
this.state.selectedImage.classList.remove('active')
target.classList.add('active')
}
}
But I will recommend against it..
To be honest, you should not add your class with DOM manipulating but instead add it in your render <img className={this.state.selectedImage === myImage ? 'active' : undefined} />
You are shadowing your event in this code:
selectHandler(event){
this.setState({
selectedImage: event.target
}, (event) => {
this.markSelectedHandler(event)
})
}
You need not to shadow, by not passing a parameter with the same name (event to the setState callback):
selectHandler(event){
this.setState({
selectedImage: event.target
}, () => {
this.markSelectedHandler(event)
})
}
Try not sending event as an argument to the callback
eg you have written
selectHandler(event){
this.setState({
selectedImage: event.target
}, (event) => {
this.markSelectedHandler(event)
})
}
Write like this instead
selectHandler(event){
this.setState({
selectedImage: event.target
}, () => {
this.markSelectedHandler(event)
})
}
I'm stuck at a crossroads with a component I am working on.
I have the following component "RecentUpdates"
Within it I am passing props down to a few other components, as you can see from the top of the file.
My problem is when adding a new post, I can not figure out how to get the correct update object array back and i also can not figure out the correct 'Vue way' to update the data prop that is being passed down to the "PostList" component.
<template>
<div>
<PostFilter v-on:selectedCategory="getSelectedPosts" v-on:showAllPosts="showAllPosts" :user="user" :categories="categories"/>
<PostList v-if="recent_posts[0]" :categories="categories" :posts="recent_posts[0]" :user="user"/>
<Pagination v-on:getPreviousPage="getPreviousPage" v-on:getNextPage="getNextPage"/>
</div>
</template>
<script>
import PostList from './PostList';
import PostFilter from './PostFilter';
import Pagination from './Pagination';
import EventBus from '../event-bus';
export default {
name: 'RecentUpdates',
data: () => ({
errors: [],
recent_posts: [],
}),
props: ['categories', 'user'],
components: {
PostList,
PostFilter,
Pagination
},
created() {
if (this.user.meta.selected_categories[0] == 0) {
this.showAllPosts();
}
// do not call here, not working as expected
// is switching selected category to an incorrect one
// this.updateList();
this.getSelectedCategory();
},
watch: {
recent_posts: function(newValue) {
EventBus.$on('addPost', function(newPost) {
console.log(newPost);
this.$forceUpdate();
//this.recent_posts.push(newPost);
//this.$set(this.recent_posts, newPost, newPost);
// this.$nextTick(function () {
// this.recent_posts.push(newPost);
// });
});
console.log(this.recent_posts[0]);
// this.$nextTick(function () {
// console.log(this.recent_posts[0]) // => 'updated'
// });
// if (this.user.meta.selected_categories[0] == 0) {
// EventBus.$on('addPost', this.showAllPosts);
// } else {
// EventBus.$on('addPost', this.getSelectedCategory);
// }
//this.updateList();
}
},
methods: {
// updateList() {
// if (this.user.meta.selected_categories[0] == 0) {
// EventBus.$on('addPost', this.showAllPosts);
// //EventBus.$emit('newPost');
// } else {
// EventBus.$on('addPost', this.getSelectedCategory);
// //EventBus.$emit('newPost');
// }
// },
getSelectedCategory() {
let categoryId = this.user.meta.selected_categories[0];
this.getSelectedPosts(categoryId);
},
showAllPosts() {
axios.get('/wp-json/wp/v2/posts?_embed=true&status=[publish,resolved,unresolved]',
{headers: {'X-WP-Nonce': portal.nonce}})
.then(response => {
this.recent_posts = [];
//this.recent_posts = response.data;
//console.log(response.data);
this.recent_posts.push(response.data);
console.log(this.recent_posts[0]);
})
.catch(e => {
this.errors.push(e);
});
},
getSelectedPosts(categoryId) {
axios.get('/wp-json/wp/v2/posts?_embed=true&status=[publish,resolved,unresolved]&categories=' + categoryId,
{headers: {'X-WP-Nonce': portal.nonce}})
.then(response => {
this.recent_posts = [];
//console.log(response.data);
this.recent_posts.push(response.data);
console.log(this.recent_posts[0]);
})
.catch(e => {
this.errors.push(e);
});
},
/**
* Pagination methods
*
*/
getPreviousPage(page) {
axios.get('/wp-json/wp/v2/posts?_embed=true&status=[publish,resolved,unresolved]&page=' + page,
{headers: {'X-WP-Nonce': portal.nonce}})
.then(response => {
this.recent_posts = response.data;
})
.catch(e => {
this.errors.push(e);
});
},
getNextPage(page) {
axios.get('/wp-json/wp/v2/posts?_embed=true&status=[publish,resolved,unresolved]&page=' + page,
{headers: {'X-WP-Nonce': portal.nonce}})
.then(response => {
this.recent_posts = response.data;
})
.catch(e => {
this.errors.push(e);
});
}
},
}
</script>
<style>
</style>
So there are a number of issues I see reading through your code.
You have a recent_posts data property, which is an array. When you make your ajax call to get the posts you push the response which is also an array into the recent_posts array. Why? Why not just set recent_posts = response.data? Then you won't have to be passing recent_posts[0] around.
You're setting up your EventBus handler inside a watcher. This is really unusual. Typically you would set up a handler inside created or mounted.
this inside the EventBus handler likely refers to the EventBus and not your Vue. Ideally, you would set the handler to be a method on the component, which is already bound to the Vue. Something like EventBus.$on("addPost", this.addPost).
Once you've done all that, adding a new post should be as simple as this.recent_posts.push(newPost).
Here is what I might recommend.
export default {
name: 'RecentUpdates',
data(){
return {
errors: [],
recent_posts: []
}
},
props: ['categories', 'user'],
components: {
PostList,
PostFilter,
Pagination
},
created() {
if (this.user.meta.selected_categories[0] == 0) {
this.showAllPosts();
}
this.getSelectedCategory();
EventBus.$on("addPost", this.addPost)
},
beforeDestroy(){
EventBus.$off("addPost", this.addPost)
},
methods: {
getPosts(url){
axios.get(url, {headers: {'X-WP-Nonce': portal.nonce}})
.then(response => this.recent_posts = response.data)
.catch(e => this.errors.push(e))
},
showAllPosts() {
const url = '/wp-json/wp/v2/posts?_embed=true&status=[publish,resolved,unresolved]';
this.getPosts(url);
},
getSelectedPosts(categoryId) {
const url = '/wp-json/wp/v2/posts?_embed=true&status=[publish,resolved,unresolved]&categories=' + categoryId;
this.getPosts(url);
},
addPost(newPost){
this.recent_posts.push(newPost)
},
... //other methods
},
}
Try using kebab-case in your event listeners instead of camelCase:
Example: v-on:selectedCategory="getSelectedPosts" should be v-on:selected-category="getSelectedPosts".
Example: v-on:showAllPosts="showAllPosts" should be v-on:show-all-posts="showAllPosts" or even using the shortcut #show-all-posts="showAllPosts".
UPDATE: If you can provide the code of the other components so we can have a clearer vision of your problem, But you only want to track changes that happens on an object or an array in vue.js you need to deep watch them.
your watcher should be :
watch: {
recent_posts: {
deep: true,
handler: function( oldValue, newValue) {
console.log( "recent_posts has changed" );
// A post has been added, updated or even deleted
}
}
}