Ampersand state - how to observe State prop changes from an external State? - ampersand.js

Assuming that view bindings have to observe model props how can I trigger State change from State change in another State? might the the 2nd State be a derived prop or a child prop for example?

Correct, Ampersand-view bindings must observe current model (ampersand-state).
In order for current State to "listen" to another State, you can include the 2nd State as a prop of the current State using data-type "state", for example:
import State from 'ampersand-state';
export default State.extend({
__name: 'ConnectionState',
idAttribute: 'site_id',
props: {
site_id: ['any', true],
message_group_id: ['any', true],
message_group: ['state'],
Then when you initialize states, set the state prop with 2nd State:
let messageGroups = new MessageGroupsCollection(state.messageGroups);
let connections = new ConnectionsCollection(state.connections);
connections.forEach((model) => {
model.set('message_group', messages.get(model.get('message_group_id')));
});
Now in your view (backed by State) you can bind to the other State like:
bindings: {
'model.message_group.is_hired': {
hook: 'isHired',
type: 'toggle'
}
},
Now when you make changes to message_group.is_hired, it will reflect in Connection bindings.

Related

How to add listener on change Object which already defined with #observable decorator in MobX to use in React

I have a React component that connected to MobX state with inject from "mobx-react", all works fine.
Now I need to know that my object was changed to render another interface.
I think that MobX must have the ability to do it's without compare objects, in docs I see observable and observe functions, but it not work for me.
I write a very simplified example of what I want:
class Example {
#observable
myObject = {
prop: 'some string',
...there are many properties
}
#observable
myObjectWasChanged = false;
}
The question: What i can to do to set myObjectWasChanged to true when myObject was changed?
Now React rerender component when any #observable changed, with this I have not any problems, but how to know that myObject was changed without compare, (in componentDidUpdate for example)
You could probably use observe or intercept methods.
import { observable, observe } from "mobx"
const person = observable({
firstName: "Maarten",
lastName: "Luther",
})
observe(person, (change) => {
console.log(change.type, change.name, "from", change.oldValue, "to", change.object[change.name])
})
More info here: https://mobx.js.org/refguide/observe.html

Part of the redux state re-renders (unnecessary) whole react component

One of my redux state part (reducer) is filters reducer, used to store filter settings per page. It's key-object structure, so it looks like this:
{
dashboard: {
minDate: '2017-01-01',
maxDate: '2019-01-01',
//... other filters
},
otherPageKey: {
//... other filters
}
}
My dashboard page is big, but it contains select which value is read from reducer: filters.dashboard.minDate. Code that is responsible for connection:
function mapStateToProps({
filters
}) {
return {
filters: filters.dashboard
};
}
Now - whenever we select new date from that select component, whole filters tree is changed, so the whole dashboard component is being re-renderd.
How can I solve this problem? Expected result is, that only select component, whose property is changed by user, should be re-rendered.
More code would be helpful, but this is usually caused by the state or props being detected by a parent component (in this case, dashboard).
This can be caused by a few things.
You're mapping to parent component rather than the specific child one. This means the parent component is detecting the change and therefore re-rendering. Is your mapStateToProps in your dashboard component as opposed to just in your select component?
Improper use of connect or mapDispatchToProps. Check out this post for more information.
Again, more code would be helpful but I hope this helps.

Can't update the specific key of state variable

So, I have a checkbox having onchange method but in the method, I am not able to change the value.
Also, the value received is the same as key in the state variable.
Following the code:
constructor(props){
super(props)
this.state={pins:[],filter:{Irrigation:true,Patvan:true,Drinking:true,Minigrid:true,Rooftop:true}}
this.handleFilterChange=this.handleFilterChange.bind(this)
}
handleFilterChange(filtervalue){
console.log(filtervalue) //lets assume value to be Drinking
console.log(this.state.filter[filtervalue]) // it says true
this.setState({filter[filtervalue]:!this.state.filter[filtervalue]}) //here the error occurs
}
I know i am doing some syntax mistake. Help in pointing it out. Thanks
You can't update the state using filter[filtervalue] as property key, you should do something like this:
this.setState(
prevState => ({
...prevState,
filter: {
...prevState.filter,
[filtervalue]: !prevState.filter[filtervalue]
}
})
)
First, if you need to access the previous state during an update of the state, you should use the method tecnique, not the object one.
Then, updating an inner property of the state can be a bit cumbersome as you see, my suggestion is to use something like immer.
First of all when your state changes depend on the current state use the updater method of setState. After that since you are updating a nested object you need to recreate the nesting up to the property you need to update.
In your case the object passed to setState should be
{
filter: {
Irrigation: true,
Patvan: true,
Drinking: false, // this is what changed
Minigrid: true,
Rooftop: true
}
}
So your actual setState call should be
this.setState(({
filter // destructure the original state and extract the filter property
}) => ({
filter: {
...filter, // spread the existing filter properties
[filtervalue]: !filter[filtervalue] // overwrite the property that changed
}
}))

React Component state value not updated, when setState is called on jsonObject's property

I am working on three properties of JSON object which returns boolean values.
updateChange=(value) =>{
//Making copy of existing json Object
let newState = JSON.parse(JSON.stringify(this.state.mainValue));
// chaning property of cellular over here
newState.cellular = value;
console.log(newState);
this.setState({mainValue:newState});
//I tried setState in many different ways. But Component is not changing state value
console.log(this.state.mainValue)
};
I found why this is happening. I am using getDerivedStateFromProps in this component to look at changes made by parent to update values. This is avoiding changes made to state of the child. So I have created a state called previous state to compare the previous props and present props from parent to render. This avoids component to refresh when ever it local state values changes.
static getDerivedStateFromProps(propsnow,state){
if(state.previous !== propsnow.detailSwitches){
return{
previous :propsnow.detailSwitches,
cellular: propsnow.detailSwitches.cellular,
wifi1: propsnow.detailSwitches.wifi1,
wifi2: propsnow.detailSwitches.wifi2
};
}
return null;
}
Any examples or better practices can be helpful. Thanks
You are ONLY setting the 'mainValue' variable of your state to the value of newstate, shouldn't you be updating the whole state?
this.setState({...newState})
You can try this
this.setState(prevState => ({
mainValue: {
...prevState.mainValue,
cellular: value
}}))
It is an immutable way to update state.

Pass props between three components

I need to show data depending on an active device (which can be iphone or ipad). I have 3 components: App, DeviceCheck and Collection. Device check is a component with 2 buttons, Iphone and iPad. In a Collection component I'm parsing data from an external file, for example something like:
const applications = [
{
device: 'iPhone',
icon: icon,
title: 'title',
descr: 'descr',
category: 'Games',
link: 'link-to-appstore',
price: 0.99,
purchases: true
},
{
device: 'iPad',
icon: icon2,
title: 'title2',
descr: 'descr2',
category: 'Games',
link: 'link',
price: 1.99,
purchases: false,
}
]
The App.js structure is:
<DeviceCheck />
<Collection />
How do I show the iPhone or iPad data in Collection component, depending on which button was clicked in DeviceCheck component?
Create a component which passes a callback to the device check.
Use this callback to update the state of the container from the device check.
Use the state in the container to set the props of the collection.
This is very common in React and is the basis of how the compositional pattern works. If you need to share data between two components just put them in a container and lift the state to the parent component. These components are usually called containers and there is a bunch of documentation on it.
This is a good starting point: https://reactjs.org/docs/lifting-state-up.html
A rough layout would be something like this.
class Container extends React.Component {
constructor(props) {
super(props);
// Don't forget to bind the handler to the correct context
this.changeDevice = this.changeDevice.bind(this);
}
changeDevice(device) {
this.setState({device: device});
}
render() {
return (
<DeviceCheck btnCb={this.changeDevice} />
<Collection device={this.state.device} />
)
}
}
Maintain a variable in the state of App.js called selectedDevice.
In the click handler of the buttons call setState() to modify selectedDevice in state.
Use selectedDevice to show the corresponding data in <Collection />
It's a little broad your question, but you can hold a state object with the selected device and use setState on <DeviceCheck /> button click to set the selected one. and <Collection {...selectedDevice} /> will use the state object as input.
In the App component you can have deviceType as the state and a function which sets the deviceType based on the parameter and this function should be passed as props to the DeviceCheck component. Upon clicking button the in the DeviceCheck button, you need to invoke the prop function with corresponding button type as parameter. The deviceType state should be passed onto the Collection component as prop and based on the prop value, the Collection component can render the respective Device data. Hope this helps!

Resources