Reactjs: Why my props aren't being updated? - javascript

I need to update my props in order to render new data.
I have a view with a list of dealers for a casino game. That list is OK, the problem comes up when you try to add a new dealer, the dealer doesn't display in the view, yo have to reload the page in order to see the new change.
Let me show you my code starting in the actions
action
class CreateDealersActions {
constructor () {
this.generateActions('createDealerSuccess');
}
createDealer (data) {
const that = this;
that.dispatch();
axios.post(`${API_ENDPOINT}/create-dealer/create-dealer`, data)
.then(function success (response) {
that.actions.createDealerSuccess({response});
})
}
}
store
class GetDealersStore {
constructor () {
this.state = {
dealerData : null,
};
}
#bind(GetDealersActions.dealerDataSuccess)
dealerDataSuccess (data) {
this.setState({
dealerData : data,
});
}
#bind(CreateDealersActions.createDealerSuccess)
createDealerSuccess (data) {
this.setState({
dealerData : data,
});
}
}
the dealerDataSuccess() is the function I call when the view loads in order to render the list of dealers, and createDealerSuccess() is the one called when you attempt to add a new dealer.
here you will see what every function returns
if you put in dealerDataSuccess() console.log(JSON.stringify(data));
{"dealersData": [{
"DealerId":"1",
"DealerName":"Carmen",
"NickName":"Carmen",
"Picture":"url",
"Active":"1",
"LegalId":"13",
"TypeId":"1"}
]}
but if you put in createDealerSucess() console.log(JSON.stringify(data)); it returns something like this:
{
"response": {
"data": {
"success": "New dealer successfully inserted."
},
"status": 200,
"statusText": "OK",
"headers": {
"content-type": "application/json; charset=utf-8"
},
"config": {
"method": "post",
"headers": {
"Content-Type": "application/json;charset=utf-8"
},
"transformRequest": [
null
],
"transformResponse": [
null
],
"url": "http://localhost:1102/services/create-dealer/create-dealer",
"data": {
"DealerName": "my Name",
"CardId": "1221",
"NickName": "newName",
"Picture": "url",
"Active": "1",
"LegalId": "321321",
"TypeId": "1"
}
}
}
}
the component code
component
#connectToStores
export default class Dealers extends Component {
static contextTypes = {
router : React.PropTypes.func,
}
constructor (props) {
super(props);
this.state = {}
}
componentWillMount () {
GetDealersActions.getDealers();
}
static getStores () {
return [ GetDealersStore ];
}
static getPropsFromStores () {
return {
...GetDealersStore.getState(),
}
}
render () {
return (<html for component>);
}
_addDealer = () => {
CreateDealersActions.createDealer({
DealerName : this.refs.DealerName.getValue(),
CardId : this.refs.CardId.getValue(),
});
}
}
now in the component part there is componentWillMount(), if you put console.log(JSON.stringify(this.props)); it returns
{"params":{},"query":{},"dealerData":null}
and if you put in _addDealer console.log(JSON.stringify(this.props)); where it suppose to add the new dealer you get the whole props but without the last dealer you add, so you have to refresh the page in order to see the new dealer in the view/screen.
What do you think is going on here ?
PS: if you similar question about this from me, take into account that in the other question I was 2 using different stores, here I am using just one
EDIT
the Dealers component is within a tab named management, which is this one:
const menuItems = [
{ route : 'dealers', text : 'Dealers' },
{ route : 'game-info', text : 'Game Info' },
{ route : 'player-info', text : 'Players Info' },
{ route : 'money', text : 'Money' }
];
export default class Management extends React.Component {
static propTypes = {
getActivePage : React.PropTypes.func,
menuItems : React.PropTypes.arrayOf(React.PropTypes.object),
}
static contextTypes = {
router : React.PropTypes.func,
}
render () {
return (
<div>
<TabsMainMenu menuItems={menuItems} getActivePage={this._getActivePage} />
<RouteHandler />
</div>
);
}
_getActivePage = () => {
for (const i in menuItems) {
if (this.context.router.isActive(menuItems[i].route)) return parseInt(i, 10);
}
}
}

Related

Pinia|Vue3 I can't access the property of the object that returned from the Pinia action

first of all I am using the Mockjs to simulate the backend data:
{
url: "/mockApi/system",
method: "get",
timeout: 500,
statusCode: 200,
response: { //
status: 200,
message: 'ok',
data: {
'onlineStatus|3': [{
'statusId': '#integer(1,3)',
'onlineStatusText': '#ctitle(3)',
'onlineStatusIcon': Random.image('20*20'),
'createTime': '#datetime'
}],
'websiteInfo': [{
'id|+1': 1,
}]
}
}
}
the data structure would be: https://imgur.com/a/7FqvVTK
and I retrieve this mock data in Pinia store:
import axios from "axios"
import { defineStore } from "pinia"
export const useSystem = defineStore('System', {
state: () => {
return {
systemConfig: {
onlineStatus: [],
},
}
},
actions: {
getSystemConfig() {
const axiosInstance = axios.interceptors.request.use(function (config) {
// Do something before request is sent
config.baseURL = '/mockApi'
return config
}, function (error) {
// Do something with request error
return Promise.reject(error);
})
axios.get('/system/').then(res => {
this.systemConfig.onlineStatus = res.data.data.onlineStatus
})
// console.log(res.data.data.onlineStatus)
axios.interceptors.request.eject(axiosInstance)
}
}
})
I use this store in the parent component Profile.vue:
export default {
setup() {
const systemConfigStore = useSystem()
systemConfigStore.getSystemConfig()
const { systemConfig } = storeToRefs(systemConfigStore)
return {
systemConfig,
}
},
computed: {
getUserOnlineStatusIndex() {
return this.userData.onlineStatus//this would be 1-3 int.
},
getUserOnlineStatus() {
return this.systemConfig.onlineStatus
},
showUserOnlineStatusText() {
return this.getUserOnlineStatus[this.getUserOnlineStatusIndex - 1]
},
},
components: {UserOnlineStatus }
}
template in Profile.vue I import the child component userOnlineStatus.vue
<UserOnlineStatus :userCurrentOnlineStatus="userData.onlineStatus">
{{ showUserOnlineStatusText }}
</UserOnlineStatus>
here is what I have got https://imgur.com/fq33uL8
but I only want to get the onlineStatusText property of the returned object, so I change the computed code in the parent component Profile.vue:
export default {
setup() {
const systemConfigStore = useSystem()
systemConfigStore.getSystemConfig()
const { systemConfig } = storeToRefs(systemConfigStore)
return {
systemConfig,
}
},
computed: {
getUserOnlineStatusIndex() {
return this.userData.onlineStatus//this would be 1-3 int.
},
getUserOnlineStatus() {
return this.systemConfig.onlineStatus
},
showUserOnlineStatusText() {
return this.getUserOnlineStatus[this.getUserOnlineStatusIndex - 1]['onlineStatusText']//👀I chage it here!
},
},
components: {UserOnlineStatus }
}
but I will get the error in the console and it doesn't work:
https://imgur.com/Gb68Slk
what should I do if I just want to display the specific propery of the retrived data?
I am out of my wits...
I have tried move the store function to the child components, but get the same result.
and I google this issue for two days, nothing found.
Maybe it's because of I was trying to read the value that the Profile.vue hasn't retrieved yet?
in this case, how could I make sure that I have got all the value ready before the page rendered in vue3? Or can I watch this specific property changed, then go on rendering the page?
every UX that has data is coming from remote source (async data) should has spinner or skeleton.
you can use the optional chaining for safe access (if no time to await):
return this.getUserOnlineStatus?.[this.getUserOnlineStatusIndex - 1]?.['onlineStatusText']

How to save formData from react class component?

I include my code. Can you tell me how do I save? After clicking the save button FormData is null. How do upload images using the Cloudinary library and React class component? API is working properly. Body data pass to the API as form-data. This is my API response =>
{
"code": 200,
"success": true,
"status": "OK",
"ItemDetails": {
"_id": "6125cc9ecfde9b2eb45d1909",
"item_name": "Chicken Rices",
"item_type": "Rice",
"item_time": "Lunch",
"item_price_type_small": 120,
"item_price_type_medium": 170,
"item_price_type_large": 200,
"item_url": "https://res.cloudinary.com/doelyvv2h/image/upload/v1629867165/MenuList/ldmny2cxvhqh7fisy1ex.jpg",
"cloudinary_id": "MenuList/ldmny2cxvhqh7fisy1ex",
"createdAt": "2021-08-25T04:52:46.726Z",
"updatedAt": "2021-08-25T04:52:46.726Z",
"__v": 0
},
"message": "Item added successfully."
}
import React, { Component } from "react";
import { Link } from "react-router-dom";
import User from "../../../assets/images/user.png";
import { toast } from "react-toastify";
import { APIURL } from "../../API/environment";
const initialState = {
item_name: "",
item_type: "",
item_time: "",
item_price_type_small: "",
item_price_type_medium: "",
item_price_type_large: "",
image: "",
};
class AddFoodMenu extends Component {
constructor(props) {
super(props);
this.state = initialState;
this.onChange = this.onChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
}
onChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
onSubmit(event) {
event.preventDefault();
let item_name = this.state.item_name;
let item_type = this.state.item_type;
let item_time = this.state.item_time;
let item_price_type_small = this.state.item_price_type_small;
let item_price_type_medium = this.state.item_price_type_medium;
let item_price_type_large = this.state.item_price_type_large;
let image = this.state.image;
console.log("object", item_name);
let formData = new FormData();
formData.append("item_name", item_name);
formData.append("item_type", item_type);
formData.append("item_time", item_time);
formData.append("item_price_type_small", item_price_type_small);
formData.append("item_price_type_medium", item_price_type_medium);
formData.append("item_price_type_large", item_price_type_large);
formData.append("image", image);
console.log(formData);
axios.post(`${APIURL}/menulist/add-item-to-menu`,
{ method : "POST",
body : formData}
).then((res) => {
console.log("res", res);
if (res.data.code === 200) {
toast.success(res.data.message);
window.setTimeout(function () {
window.location.href = "/login";
}, 2000);
} else {
toast.error(res.data.message);
}
});
}
I couldn't understand you're question. Post full code or some more clarity.
My suggestion would be to set the value to a state with API response.

MBDReact: How do I make a <datatable> row clickable?

I have setup my table in admin side of our application with MDBReact using the datatable. This table shows some small details of the stories that I have.
Now I have to make a row clickable i.e. add onClick to make a function call with the story id passed as an argument to this function.
Question:
How do I add onClick event to the datatable row?
(Below is my code.)
class Posts extends Component {
componentDidMount() {
this.getPosts();
}
getPosts = async () => {
const response = await fetch("http://****************/get_posts");
const post_items = await response.json();
this.setState({ posts: post_items.result }, () => {
console.log(this.state.posts);
this.setState({ tableRows: this.assemblePost() });
});
};
assemblePost = () => {
let posts = this.state.posts.map((post) => {
let mongoDate = post.dateAdded.toString();
let mainDate = JSON.stringify(new Date(mongoDate));
return {
postTitle: post.storyTitle,
// postDescription: post.storyDescription,
dateAdded: mainDate.slice(1, 11),
thankedBy: post.thankedBy.length,
reportedBy: post.reportedBy ? post.reportedBy.length : "",
userEmail: post.userEmail[0],
categoryName: post.categoryName[0],
};
});
console.log(posts);
return posts;
};
state = {
posts: [],
tableRows: [],
};
render() {
const data = {
columns: [
{
label: "Story Title",
field: "postTitle",
},
{ label: "Category Name", field: "categoryName" },
{
label: "User Email",
field: "userEmail",
},
{
label: "Date Added",
field: "dateAdded",
},
{
label: "Thanked",
field: "thankedBy",
},
{
label: "Reported",
field: "reportedBy",
},
],
rows: this.state.tableRows,
};
return (
<div className="MDBtable">
<p className="posts">Posts List</p>
<MDBDataTable striped bordered hover data={data} />
</div>
);
}
}
export default Posts;
To pull this off, here's what I did, but you'll need to appreciate these:
MDBDataTable requires you to manually define the columns and rows.
For data to render seamlessly, you define columns.field that correspond to rows[key]
Now, here's the logic, if you define a rows[key] that does not correspond to any columns.field, then that rows[key] is defined for an entire row just like we often pass index when working with map().
So based on the above observations,you can just pass the click event as a key/value pair to the row.And it will work just fine.
// ...
assemblePost = () => {
let posts = this.state.posts.map(
(post, i) => {
let mongoDate = post.dateAdded.toString();
let mainDate = JSON.stringify(new Date(mongoDate));
return {
index: i + 1, // advisable to pass a unique identifier per item/row
clickEvent: () => this.handleClick(storyId), // pass it a callback function
postTitle: post.storyTitle,
// ...others
categoryName: post.categoryName[0],
};
});
console.log(posts);
return posts;
};
// ...
Notice this clickEvent: () => this.handleClick(storyId), will be attached to the entire row.

How to get the checked tree structure values using angular2-tree plugin

Here I am generating a dynamic tree structure using my json and angular2 - tree component and till every thing is fine now, I am unable to generate the selection events ad when ever we select the events that particular names have to be selected as objects if child is there and I tried this URL and in the documentation also I didn't find any methods for getting the selected valules so please, suggest me on that.
https://angular2-tree.readme.io/docs
below is my code
options = {
useCheckbox: true
};
nodes;
data = {
"info": {
"laptop": {
},
"config": {
"properties": {
"ram": {
},
"processor": {
},
"hdd": {
}
}
},
"link": {
},
"name": {
},
"company": {
"properties": {
"model": {
},
"maker": {
"type": "integer"
},
"country": {
"type": "text"
},
"enterprise": {
}
}
}
}
};
check(){
const results = Object.keys(this.data.info).map(k => ({
name: k,
children: this.data.info[k].properties
? Object.keys(this.data.info[k].properties).map(kk => ({ name: kk }))
: []
}));
this.nodes = results;
}
.html code
<button type="button" (click)="check()">click</button>
<hr>
<input id="filter" #filter (keyup)="tree.treeModel.filterNodes(filter.value)" placeholder="filter nodes" />
<button (click)="tree.treeModel.clearFilter()">Clear Filter</button>
<tree-root #tree [focused]="true" [options]="options" [nodes]="nodes"></tree-root>
stackblitz link
https://stackblitz.com/edit/angular-kh28sg
I don't know if there a better way You can access to the selected nodes using tree.treeModel.selectedLeafNodeIds. You must before check is isSelected or not
See the docs/API
For example if you has a button
<!--I like pass as argument the property "treeModel" of #tree ("reference variable")-->
<button (click)="click(tree.treeModel)">sendData</button>
<tree-root #tree [focused]="true" [options]="options" [nodes]="nodes"></tree-root>
Your function click can be like
click(tree:TreeModel)
{
console.log(tree.activeNodes);
Object.keys(tree.selectedLeafNodeIds).forEach(x=>{
let node:TreeNode=tree.getNodeById(x);
if (node.isSelected)
{
console.log("Selected:",node.data.name,
"Parent:",node.parent.data.name);
}
})
}
NOTE: I forked your stackblitz
Updated create an object with the response
click(tree: TreeModel) {
console.log(tree.activeNodes);
let result: any = {} //<--declare a variable
Object.keys(tree.selectedLeafNodeIds).forEach(x => {
let node: TreeNode = tree.getNodeById(x);
if (node.isSelected) {
console.log("Selected:", node.data.name,
"Parent:", node.parent.data.name);
if (node.parent.data.name) //if the node has parent
{
if (!result[node.parent.data.name]) //If the parent is not in the object
result[node.parent.data.name] = {} //create
result[node.parent.data.name][node.data.name] = true;
}
else {
if (!result[node.data.name]) //If the node is not in the object
result[node.data.name] = {} //create
}
}
})
console.log(result);
}
I couldn't get the accepted answer to work, my tree.getNodeById(x) would always return null
So I used the action mapping:
Add IActionMapping to your Tree component
actionMapping: IActionMapping = {
// ...
checkboxClick: (tree, node) => {
node.data.checked = !node.data.checked;
this.setCheckedNodes(node.id, node.data.checked);
}
}
Method to store the values in a service:
private setCheckedNodes(id: string, checked: boolean) {
if (!this.treeService.selectedIds) {
this.treeService.selectedIds = new Array<[string, boolean]>();
}
const checkedNode = this.treeService.selectedIds.find(cn => cn[0] === id);
if (checkedNode) {
if (checkedNode[1] !== checked) {
checkedNode[1] = checked;
this.treeService.selectedIds[id] = checkedNode;
}
} else {
this.treeService.selectedIds.push([id, checked]);
}
}
Then on some select event get or emit the values stored in the service:
export class treeService {
// ...
public selectedIds: Array<[string, boolean]>;
}
Probably want to clear them afterwards

Why does vue.js alter my JSON data?

I want to return data from my API and display it in a vue.js project.
The data is:
{
"pages":[
{
"id":3,
"slug":"over-mij",
"updated_at":"2017-12-25T11:16:21+01:00",
"created_at":"2017-12-25T10:56:21+01:00",
"title":"Over mij",
"content":""
},
{
"id":6,
"slug":"mijn-cv",
"updated_at":"2017-12-25T11:07:29+01:00",
"created_at":"2017-12-25T11:07:29+01:00",
"title":"Mijn cv",
"content":null
}
],
"current_user":null
}
Then I'm using vue-resource to load the data from my API page in a seperate module of vuex:
const actions = {
initPages({commit}) {
Vue.resource('pages').get({}).then(function(response) {
if (response) {
response.json().then(data => {
state.pages = data;
});
}
});
}
}
The problem however is when I check what's inside the state.pages, this contains:
{
"pages": {
"pages": [
{
"id": 3,
"slug": "over-mij",
"updated_at": "2017-12-25T11:16:21+01:00",
"created_at": "2017-12-25T10:56:21+01:00",
"title": "Over mij",
"content": ""
},
{
"id": 6,
"slug": "mijn-cv",
"updated_at": "2017-12-25T11:07:29+01:00",
"created_at": "2017-12-25T11:07:29+01:00",
"title": "Mijn cv",
"content": null
}
],
"current_user": null
}
}
For some weird reason my JSON response is altered, I've already tried setting state.pages to data.pages and this just eliminates the first pages object but not the second one.
When I set my state.pages to data.pages[0] it does work, does anyone know why this is happening?
Even when I use mutations, I still have the same issue:
module of store.js:
import Vue from 'vue';
const state = {
pages: []
}
const mutations = {
'setPages' (state, pages) {
state.pages = pages;
}
}
const getters = {
pages(state) {
return state.pages;
},
page(state) {
return keyword => state.pages.find(function(page) {
return page.slug === keyword;
});
}
}
const actions = {
initPages({commit}) {
Vue.resource('pages').get({}).then(function(response) {
if (response) {
response.json().then(data => {
commit('setPages', data.pages);
});
}
});
}
}
export default {
state,
getters,
mutations,
actions
}

Categories

Resources