I have following array of data.
const filterToolTips = [{
remember_tip: 'some remember tip data.'
},
{
background_tip: 'some background tip data'
},
{
remember_on: 'some remember on tip data'
},
{
remember_off: 'some remember off data.'
},
{
background_on: 'some background on data '
}
];
I am trying to get each key text for different use case.
So, I am using lodash, But, It is showing undefined.
const toolText = get(filterToolTips,'remember_tip');
console.log('toolTipText', toolTipText); // 'toolTipText', undefined
Even I tried find too.
Any suggestions?
Use _.find() with _.has() to find an object with the requested key, and then use _.get() to take the value:
const filterToolTips = [{"remember_tip":"some remember tip data."},{"background_tip":"some background tip data"},{"remember_on":"some remember on tip data"},{"remember_off":"some remember off data."},{"background_on":"some background on data "}];
const key = 'remember_tip';
const toolText = _.get(
_.find(filterToolTips, o => _.has(o, key)),
key
);
console.log(toolText);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
However, this is a really weird data structure, and you should transform it (or ask the server guys to send you something more usable). This solution converts the data structure to a Map, and then gets the value:
const filterToolTips = [{"remember_tip":"some remember tip data."},{"background_tip":"some background tip data"},{"remember_on":"some remember on tip data"},{"remember_off":"some remember off data."},{"background_on":"some background on data "}];
const tipsMap = new Map(Object.entries(Object.assign({}, ...filterToolTips)));
const key = 'remember_tip';
const toolText = tipsMap.get(key);
console.log(toolText);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
Related
OK, say I have an initial state in our Redux store that looks like this:
const initialState = {
userReports: [],
activeReport: null,
}
userReports is a list of reports. activeReport is one of those reports (the one that is actively being worked with).
I want the active report to point to one in the array. In other words, if I modify the active report, it would modify one in the userReports array. This means, the two objects must point to the same memory space. That's easy to set up.
The alternative to this approach would be to copy one of the reports that is in the userReports array and set it as the active report (now it has a different memory address). The problem is now, when I edit the activeReport, I also have to search through the array of userReports, find the report that resembles the active report and modify it there too. This feels verbose.
Here is the question:
Would it be bad practice to have the activeReport point to a report in the array (same object). When I want to change the report I could do something like this (example is using redux thunk):
export const updateReport = (report) => async (dispatch, getState) => {
try {
const report = getState().reports.activeReport
// modify the active report here
report.title = "blah blah blah"
dispatch({ type: ACTIONS.UPDATE_REPORT, payload: report })
} catch (error) {
console.log(`ERROR: ${error.message}`)
}
}
And in my reducer:
case ACTIONS.UPDATE_REPORT:
return { ...state, activeReport: action.payload }
as you can see, after updating the report I still return a "new version" of that report and set it as active, but this approach also updates the report in the userReports array because they point to the same memory address.
I would say thats not ideal, do the reports have id's? If they do I would rather hold the userReports in an object with keys being the id's, then active report can just be an id and renamed to activeReportId so you can fetch the activeReport with userReports[activeReportId]
You also asked for reasons:
So firstly any screen that looks at userReports wont rerender because the reports aren't being reassigned.
Secondly if someone later wants to update those screens they will reassign userReports which could cause problems.
Thirdly its an unusual pattern which is a huge no no for redux. The point of redux is that it has a very obvious pattern so when you add things to it you don't have to think and can just make changes with confidence.
Your activeReport should not be pointing to an object in the userReports array, but rather it should be an id of the report, which the user is currently working on. Each of the report in the userReports will have a unique id field to identify the report - this would be helpful when rendering in react - this id field can be used as key.
Then your action creator/dispatcher will look like this:
export const updateReport = (updatedReport) => async (dispatch, getState) => {
dispatch({ type: ACTIONS.UPDATE_REPORT, payload: updatedReport });
}
You will call this on change in your component:
const onTitleChangeHandler = (e) => {
var newTitle = e.target.value;
// you will get the userReports and activeReport from props or by using some redux selector, also you will need to get dispatch and getState from redux
var activeReportObj = userReports.filter((r) => r.id === activeReport)[0];
updateReport({ title: newTitle, ...activeReportObj })(dispatch, getState);
}
Lastly, your reducer will be:
case ACTIONS.UPDATE_REPORT:
var newUserReports = state.userReports.map((r) => {
if (r.id === state.activeReport) {
return action.payload;
}
return r;
});
return { newUserReports, ...state };
I have a function that runs every time your location changes and I'm trying to set a value in my array when a certain if statement is found true. All I seem to be doing is removing everything from my variable except the value that I am changing. Bad explanation so here is some code...
The data starts like this:
this.state = { selectedItem: [] }
And will change to something like this during normal app use:
selectedItem: [{address: 'Somewhere', latitude: -37.826835, longitude: 144.992030, found: false }]
Here is where I am trying to change the data (This will always run after some data is added):
const newSelectedItem = () => {
let copyB = {...this.state.selectedItem};
copyB.found = true;
return copyB;
};
this.setState({selectedItem: newSelectedItem});
When I try to run:
{this.state.selectedItem.address}
I see the initial value which would be the address "Somewhere" but when my function runs based on location change it disappears. What have I actually done to my data in my above function?
Have I just made it selectedItem: [{found:true}] or something dumb like that?
You were doing fine except for one single thing , in this function :
const newSelectedItem = () => {
let copyB = {...this.state.selectedItem};
copyB.found = true;
return copyB;
};
this.setState({selectedItem: newSelectedItem});
here copyB is now an object , but yours selected Item was an array. So the problem is now selectedItem is now an object when you do setState with newSelctedItem.
SO copyB.found = true; wouldnt evaluate anything rather, try copyB[0].found = true; so there the value will be accessed and return true accordingly.
And when you try to access the state , replace {this.state.selectedItem.address}
with {this.state.selectedItem[0].address} ,
Hope i helps. feel free to ask any doubts.
You can play around with this pen codepen
Well, this is pretty weird since you still get the initial value. There are some problems in your code:
You want to store your variables in a array: It's fine, but the problem comes from the way you retrieve and set your value. Since selectedItem, your {this.state.selectedItem.address} because this is an object destructuring. To do that, you have to destruct your array first, e.g: item = selectedItem[0] or using map, etc... After that, you can try: item.address.
Another problem is from your newSelectedItem. Since let copyB = {...this.state.selectedItem}; will destruct your selectedItem, take all its properties and set to newSelectedItem, it will make your selectedItem become an object, not an array anymore.
If your selectedItem stores only 1 object, so don't use array. This selectedItem will become:
selectedItem: {
address: 'Somewhere',
latitude: -37.826835,
longitude: 144.992030,
found: false
}
It looks like a JSON object, hence you can do: selectedItem.address
In case you still don't get it, place a little debug or a console.log("selectedItem", this.state.selectedItem) to see what happend, and you will find out.
I think in the end I was mapping a function to the data and not the data itself. This was my eventual solution...
const newMyWaypoinys = this.state.myWaypoints.map(a => {
let copyA = {...a};
if (copyA.address === wp.address) {
if (copyA.address === this.state.selectedItem.address) {
this.setState(prevState => ({
selectedItem: {
...prevState.selectedItem,
found: true
}
}))
}
copyA.found = true;
}
return copyA;
});
this.setState({
myWaypoints: newMyWaypoinys,
});
I've got some form data that I display using a readonly input that is styled to look like plain text. When users click an edit button, they can then edit the inputs and either save or cancel.
My issue is obviously that when a user clicks cancel, the data they entered into the input remains (even though it isn't saved to the DB). I'm trying to figure out a way to reset the input to its initial data. I'm aware of this answer, but it doesn't seem to work because the data is fetched on creation.
This fiddle is similar except for the fact that the data in the real app comes from an axios call. The equivalent call is essentially:
fetch() {
axios.get(this.endpoint)
.then(({data}) => {
this.name = data.data;
});
}
Annoyingly, the fiddle actually works. However in my actual implementation it doesn't. The only difference with the app is that the data is an array.
How can I make this work?
This fiddle represents what my code actually does.
In the code:
data: () => ({
endpoint: 'https://reqres.in/api/users',
users: [],
initialData: []
}),
//...
edit: function(index) {
this.users[index].disabled = false
this.initialData = this.users
},
reset: function(index) {
this.users[index].disabled = true
this.users = this.initialData
}
Since users and initialData are arrays, you must use index when you access them.
So, at first sight, the change would be from:
this.initialData = this.users
To
this.initialData[index] = this.users[index]
But this won't work. Since this.users[index] is an object, whenever you change it, it will change what this.initialData[index] holds, since they are both just pointing to the same object. Another problem is that when you set it like that, the initialData won't be reactive, so you must use Vue.set().
Another thing, since you just want to reset the first_name property (the one you use at <input v-model="user.first_name" >), you should then assign user[].first_name to initialData[index].
Considering those changes to edit(), in the reset() method, the addition of [index] and of the .first_name field are enough. Final code:
edit: function(index) {
this.users[index].disabled = false
Vue.set(this.initialData, index, this.users[index].first_name);
},
reset: function(index) {
this.users[index].disabled = true
this.users[index].first_name = this.initialData[index]
}
JSFiddle: https://jsfiddle.net/acdcjunior/z60etaqf/28/
Note: If you want to back up the whole user (not just first_name) you will have to clone it. An change the order of the disabled property:
edit: function(index) {
Vue.set(this.initialData, index, {...this.users[index]});
this.users[index].disabled = false
},
reset: function(index) {
Vue.set(this.users, index, this.initialData[index]);
}
JSFiddle here. In the example above the clone is created using the spread syntax.
Input is immediately updating the model. If you want to do something like edit and save you have to take a copy and edit that. I use lodash clone to copy objects then update the fields back when save is clicked. (of course sending message to server.)
I have the following code where I want to fill in the id, so I'm thinking to write something like this:
const data = [
{ id: 'some-id' },
{ id: 'some-other-id' },
{ id: 'third-id' },
];
const tabIndex = R.findIndex(R.propEq('id', R.__))(data);
So I can use it like this tabIndex('third-id'), but this is not a function.
What do I miss or confuse with this?
The following works
const tabIndex = (id) => R.findIndex(R.propEq('id', id))(data);
But I thought, that is the point of R.__ gaps function.
I think that by far the simplest way to do this is
const matchId = (id, data) => R.findIndex(R.propEq('id', id), data);
matchId('third-id', data); //=> 2
If you really want to make this points-free, Ramda offers several functions to help, such as useWith and converge (for which one can often substitute lift.) This one would take useWith:
const matchId = R.useWith(findIndex, [R.propEq('id'), R.identity]);
matchId('third-id', data); //=> 3
But I find the first version much more readable. You can find both on the Ramda REPL.
Do pay attention to the side note from Emissary. The R.__ placeholder is essentially used to show gaps between the arguments you supply; as a final argument it doesn't do anything.
I'm still trying to master this dark art myself but I think the issue is that R.findIndex expects a predicate (a function / assertion) as an argument and does not differentiate between predicates and regular curried functions as input.
To resolve this a new function can be composed (evaluated right to left):
const data = [
{ id: 'some-id' },
{ id: 'some-other-id' },
{ id: 'third-id' }
];
const tabIndex = R.compose(R.findIndex(R.__, data), R.propEq('id'));
console.log(tabIndex('third-id')); // 2
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.24.1/ramda.min.js"></script>
Side Note: the R.__ placeholder is inferred automatically for missing right-most arguments - e.g. R.propEq('id') and R.propEq('id', R.__) are equivalent.
I have a 2D array of objects with key/value pairs as a state variable, and I'm trying to use the recommended way of setting/changing state variables, which is to use this.setState({x:y}) instead of directly setting it using this.state.x = y and then forceUpdate(). However, when I try to do that, it gives me an "unexpected token" error.
I basically want to flip a variable from one state to the other, so I'm usng a ternary operator. This code works
toggleBookmark(category, index) {
this.state.menuItems[category][index].bmIcon = (this.state.menuItems[category][index].bmIcon === "bookmark-o") ? "bookmark" : "bookmark-o";
}
This code, which I'd expect to do the same thing, gives an error
toggleBookmark(category, index) {
this.setState({menuItems[category][index].bmIcon: (this.state.menuItems[category][index].bmIcon === "bookmark-o") ? "bookmark" : "bookmark-o"});
}
I thought it might be the ternary operator, so I put the value into a variable and tried setting the state variable with that, but it still gives the same error.
toggleBookmark(category, index) {
var iconText = (this.state.menuItems[category][index].bmIcon === "bookmark-o") ? "bookmark" : "bookmark-o";
this.setState({menuItems[category][index].bmIcon: iconText});
}
Am I doing something wrong? Is what I want to do possible with setState()?
In Javascript, you cannot use an expression as a key for an object when creating that object inline.
The problem here is that you have done {menuItems[category][index].bmIcon: iconText} which will throw a syntax error.
If you want a quick way to solve this, you may create the object first, then assign the value to that key like this:
var state = {};
state[menuItems[category][index].bmIcon] = iconText;
this.setState(state);
It's worth noting however that ES6 Provides a sugar for doing this, and there is another answer here that might provide more insight
How do I create a dynamic key to be added to a JavaScript object variable
Update:
I now see what you meant, I had previously assumed that menuItems already defined, but what you want to do is change the value of a key inside a nested object that is in this.state
This is something that React is not really built to do, you should keep your state relatively simple, and make separate React components for each menu item, then have them manage their own state. I would strongly recommend this approach because it will keep your code clean and robust. Don't be afraid to make more components!
However if you do want to keep all this nested state in one component (not advised), then you should first make a copy of the object you want to setState on.
var newMenuItems = _.clone(this.state.menuItems);
var iconText = (this.state.menuItems[category][index].bmIcon === "bookmark-o") ? "bookmark" : "bookmark-o";
newMenuItems[category][index].bmIcon = iconText;
this.setState({ menuItems: newMenuItems });
OR
var iconText = (this.state.menuItems[category][index].bmIcon === "bookmark-o") ? "bookmark" : "bookmark-o";
this.state.menuItems[category][index].bmIcon = iconText;
this.forceUpdate();
(First method preferred, but it requires you have something like underscore or lodash installed )
I have the data chat:
chat: {
id: 'ss3k5e6j1-6shhd6-sdasd3d3-23d5-gh67',
agentName: 'egaliciar',
agentAvatar: 'http://i.imgur.com/DY6gND0.png',
messages: [
{
id: 1,
lines: [
'Me pueden ayudar?',
'Tengo problemas con mis boletos',
'Hola buen dia...',
],
time: '17:20',
},
{
id: 2,
lines: ['¿Me podria regalar su nombres', 'Con gusto...'],
time: '17:22',
date: '23/ene/2012',
},
],
},
};
and when i do
const oldLines =Object.assign({}, this.state.chat);
oldLines.messages[0].lines.push('newValue');
My state Changed..... without this.setState({});
I Made a Clone;
var clone = JSON.parse(JSON.stringify(this.state.chat));
clone.messages[0].lines.push('new Value');
and the State maintain their state;
thus, the complete solution is for me:
var clone = JSON.parse(JSON.stringify(this.state.chat));
clone.messages[0].lines.push(questionAreaMessage); //the state maintains
this.setState({chat:clone}); //here the State change!!!!