I know that MobX can detect when an object property changes (in our case myData.name), but how does MobX know the User component depends on myData.name?
class MyData {
#observable name = "John"
}
#observer
class User extends Component {
render() {
// here, User depends on myData.name, but how MobX know??
let { name } = this.props.myData;
return <div>{name}</div>
}
}
let myData = new MyData();
ReactDOM.render(<User myData={myData} />, document.getElementById('root'));
// this triggers User component to refresh, but how does MobX know User
// component depends on MyData.user?
setTimeout(function(){
myData.name = "Peter";
}, 2000)
The render() function of User is of special importance to mobx becuse you have decorated User as an observer (with #observer). As mobx docs say....
"MobX reacts to any existing observable property that is read during the execution of a tracked function."
"reading" is dereferencing an object's property, which can be done
through "dotting into" it (eg. user.name) or using the bracket
notation (eg. user['name']).
"trackable functions" are the expression of computed, the render()
method of an observer component, and the functions that are passed
as the first param to when, reaction and autorun.
"during" means that only those observables that are being read while
the function is executing are tracked. It doesn't matter whether
these values are used directly or indirectly by the tracked
function.
Related
I want to move shared functionality from my react components to a higher order component like this:
function withListFunctions(WrappedComponent) {
return class extends React.Component {
constructor(props) {
super(props);
}
// my shared functionality
deleteItem() {
// Do something, then ...
this.setState({itemDeleted: true});
}
render() {
return (
<WrappedComponent
deleteItem={this.deleteItem}
/>
);
}
}
Using this kind of syntax requires to explicitely bind this in the constructor of the HOC:
this.deleteItem = this.deleteItem.bind(this);
.. but I wanted to bind the wrapped component instead. So what I tried in my wrapped component's constructor was
this.props.deleteItem = this.props.deleteItem.bind(this);
But this just resulted in a "Cannot assign to read only property" Error, as react props are meant to be read only.
I am aware that I could store the state item in the HOC and pass it down as a prop. But it would then be not accessible(writeable) anymore by other wrapped components functions, right? I wonder if there is a way to just share the unbound function and then bind it to the wrapped instance.
[Edit] I marked Abdul Rauf's answer as "accepted", nevertheless I want to state that Karen Grigoryan's answer is the solution I am actually using because it works and seems just appropiate for my app's complexity.
I am aware that I could store the state item in the HOC and pass it
down as a prop. But it would then be not accessible(writeable) anymore
by other wrapped components functions, right?
You should store shared state in HOC. You can pass multiple state update methods to Wrapped components that they can call internally to update state indirectly.
If you don't want to pass multiple state update methods then we have 2 options:
Option 1: Create a single dispatch method in HOC that takes action and optional payload and pass it to Wrapped components.
// Do not mutate state in reducer. always return a new state
reducer(state, action) {
switch (action.type) {
case 'delete':
// return final state after delete
case 'add':
// return final state after add using action.payload
case 'update':
// return final state after update using action.payload
default:
// A reducer must always return a valid state.
// Alternatively you can throw an error if an invalid action is dispatched.
return state;
}
}
dispatch(action) {
const updatedState = this.reducer(this.state, action)
this.setState(updatedState);
}
Option 2: Use useReducer hook if you can use latest react with hooks support.
I wonder if there is a way to just share the unbound function and then
bind it to the wrapped instance.
Technically you can do that (Check Karen Grigoryan's answer). But it is considered a bad practice because of many reasons. Few of those are:
Its against encapsulation principles. (State is in Child component and state update logic is in parent component). Parent component shouldn't know anything about state of child components.
Props can change over time but this won't be reflected automatically in derived Instance properties/fields
props are read only by design.
React elements are immutable. Once you create an element, you can’t change its children or attributes. An element is like a single frame in a movie: it represents the UI at a certain point in time.
So technically one of the ways to bind deleteItem to WrappedComponent context, is to just bind it in WrappedComponent constructor:
codesandbox example
this.deleteItem = this.props.deleteItem.bind(this);
I have heard of getDerivedStateFromProps but it does not work how I want it to.
I have this code:
class App extends Component {
static getDerivedStateFromProps(props, state) {
console.log("Get derived state from props called");
return null;
}
render() {
return (
<button onClick={() => this.setState({})}>Change State</button>
);
}
}
Clicking the button calls getDerivedStateFromProps. This is not what I need. I want a function that is called only when new props are received. When internal state changes, this function should not be called.
I intend to use this in a modal form scenario. When the component receives an object as a prop, the component should translate this object into a form, then place that form into the state. If I use getDerivedStateFromProps, since it is called in every this.setState(), it would not reflect the changes in the form since whenever the user types, a this.setState() is fired, and instead of the changes being set into the state, the initial object is.
Which React lifecycle method should I use?
There are a couple of ways to approach this problem depending on different use cases.
If new props are added, those new props can be checked in the Child component and user defined functions can be called in case the new object exists.
getDerivedStateFromProps(props, state, prevProps) is a static method and this does not exist in the same, which means that you'll have to return a state based on various conditions. That is, let it work on default on most cases. On cases where you want to alter its functionality, place checks and return the modified state.
Hope it answers :)
I have a global service widgetService which holds data for a number of widgets, each identified by a widgetID. Each widget's data can change at any time. I want to display a widget with a React component, say WidgetReactComponent.
The react component shall take a widget ID as property and get the information to display from the widget service. A widget's data can be queried from the widget service with the method getWidgetData(widgetID). And in order to be able to publish data changes, it also offers two methods: addListenerForWidget(widgetID, listener) and removeListenerForWidget(widgetID, listener).
When assuming that the property is set once and never changed, this can be achieved like this, following React's recommendations:
class WidgetReactComponent extends Component {
constructor() {
super();
this.state = {
data: widgetService.getWidgetData(this.props.widgetID)
};
this._onDataChange = this._onDataChange.bind(this);
}
_onDataChange(newData) {
this.setState({data: newData});
}
componentDidMount() {
// React documentation: "This method is a good place to set up any subscriptions."
widgetService.addListenerForWidget(this.props.widgetID, this._onDataChange);
}
componentWillUnmount() {
// React documentation: "Perform any necessary cleanup in this method, such as [...] cleaning up any subscriptions that were created in componentDidMount()."
widgetService.removeListenerForWidget(this.props.widgetID, this._onDataChange);
}
render() {
return <div className="Widget">{this.state.data.stuff}</div>;
}
}
The component may then be used like this:
<ReactWidgetComponent widgetID={17} />
However, the widgetID property may change at any time, and the component has to handle this in order to function properly under all circumstances. By react's recommendation, this should be handled by setting the state based on properties using the static getDerivedStateFromProps function. But since it is static, I do not have access to the component and cannot change the listeners accordingly.
One way to work around this would be to store the widgetID in the state, and then use the lifecycle method componentDidUpdate to detect the change, like this:
constructor() {
super();
this._onDataChange = this._onDataChange.bind(this);
}
static getDerivedStateFromProps(nextProps) {
return {
widgetID: nextProps.widgetID,
data: widgetService.getWidgetData(nextProps.widgetID)
};
}
componentDidUpdate(prevProps, prevState) {
if (prevState.widgetID !== this.state.widgetID) {
widgetService.removeListenerForWidget(prevState.widgetID, this._onDataChange);
widgetService.addListenerForWidget(this.state.widgetID, this._onDataChange);
}
}
However, componentDidUpdate won't be called when shouldComponentUpdate returns false. This doesn't feel like a safe way of doing this. Also I believe the listeners will be wrong for the entire timespan between the property change and the update's completion. How could I safely implement this?
You don't need to store widgetID in state, you can compare prevProps with this.props:
componentDidUpdate(prevProps, prevState) {
if (prevProps.widgetID !== this.props.widgetID) {
widgetService.removeListenerForWidget(prevProps.widgetID, this._onDataChange);
widgetService.addListenerForWidget(this.props.widgetID, this._onDataChange);
}
}
You will also need to add the listener in componentDidMount as componentDidUpdate is not called on first rendering:
componentDidMount() {
widgetService.addListenerForWidget(this.props.widgetID, this._onDataChange);
}
Regarding your concerns:
componentDidUpdate won't be called when shouldComponentUpdate returns false
From the docs:
Use shouldComponentUpdate() to let React know if a component’s output is not affected by the current change in state or props.
So if you decided to not update the component when this.props.widgetID changes, then you are violating the assumption/purpose of shouldComponentUpdate and should not expect your widget listener to be updated.
A lot of things will not work as intended if you misuse shouldComponentUpdate anyway (eg. component not updated to reflect new data), so relying on an API being used correctly as per official docs is a necessity to achieve simplicity, rather than something to be avoided.
the listeners will be wrong for the entire timespan between the property change and the update's completion
By this logic, when you update some displayed data in an event handler, you can also claim that the data displayed is wrong for the entire timespan between the event and the re-rendering. You can even claim that your text editor is displaying the wrong data between the time you press a keyboard key and rendering of the key on the screen.
I am new to ES6 and still trying to grasp the concepts of the new specifications, i am currently working on a component in React where i need to make an ajax call and store this response in an object. Then use this object to the map the necessary elements
My component looks like the following
export class App extends Component {
search(){
//make ajax call
response = obj.responseText;
}
getValues(){}
render(){
let result = response.data.map(this.getValues);
return(
<div onKeyDown={this.search.bind(this)}>{result}</div>
)
}
}
How do i declare the "response" variable globally which gets assigned the data from ajax call "obj.responseText"?
It seems like you know what you want to achieve, but are a little confused about how to get there.
I would highly recommend reading the React documentation before you go any further.
Why not global variables?
How do I declare the response variable globally?
In short, don't. Global variables are well-documented as being evil. One instance of this component in a page with a global variable to store its search results would be fine, but imagine if you had two or more instances - they would all share/overwrite each other's search results.
Introducing state
Instead, you want to use React's component state functionality to store your search results.
You can set an initial state by setting a component's this.state in its constructor, (or in ES5, define a getInitialState method on the component).
Then, any time you want to update the component's state, you can call its this.setState(...) method, passing in a new state object. This will also trigger a re-render of the component.
Example
Here is a simple implementation following the above pattern:
export class App extends Component {
// Set the initial state of the component in the constructor
constructor(props) {
super(props);
this.state = {};
}
// This gets called when your component is mounted
componentDidMount() {
// Here we make our AJAX call. I'll leave that up to you
performMyAjaxMethodDefinedSomewhereElse(result => {
// We call this method to update `this.state` and trigger re-rendering
this.setState({ result });
});
}
render() {
// If we haven't received any results yet, display a message
if (!this.state.result) {
return (
<div>No results!</div>
);
}
// Iterate over the results and show them in a list
const result = this.state.result.map(text => (<li>{text}</li>));
// Display the result
return (
<ul>{result}</ul>
);
}
}
Naturally, if you don't want the AJAX call to fire off immediately, you can use a very similar approach, replacing componentDidMount with an event handler which looks almost identical.
I am studying the principles of react.
According to some reviews, some people says is better to keep your component stateless, what does it mean?
But other people says, that if you need to update your component, then you should learn how to set your state to the proper state.
I saw this.props / this.setProps and this.state / this.setState and I am confuse with that.
Something I am trying to figure is, how can I update a component by itself and not from a parent component? should I use props or state in this case?
I already read some docs about props and state, what I don't have clear, is: when to use one or another ?
Props vs. state comes down to "who owns this data?"
If data is managed by one component, but another component needs access to that data, you'd pass the data from the one component to the other component via props.
If a component manages the data itself, it should use state and setState to manage it.
So the answer to
how can I update a component by itself and not from a parent component? should I use props or state in this case?
is to use state.
Props should be considered immutable and should never be changed via mutation. setProps is only useful on a top-level component and generally should not be used at all. If a component passes another component a property, and the first component wants the second to be able to change it, it should also pass it a function property that the second component can call to ask the first component to update its state. For example:
var ComponentA = React.createClass({
getInitialState: function() {
return { count: 0 };
},
render: function() {
return <Clicker count={this.state.count} incrementCount={this.increment} />;
},
increment: function() {
this.setState({count: this.state.count + 1});
}
});
// Notice that Clicker is stateless! It's only job is to
// (1) render its `count` prop, and (2) call its
// `incrementCount` prop when the button is clicked.
var Clicker = React.createClass({
render: function() {
// clicker knows nothing about *how* to update the count
// only that it got passed a function that will do it for it
return (
<div>
Count: {this.props.count}
<button onClick={this.props.incrementCount}>+1</button>
</div>
);
}
});
(Working example: https://jsbin.com/rakate/edit?html,js,output)
For and object-oriented programming analogy, think of a class/object: state would be the properties you put on the class; the class is free to update those as it sees fit. Props would be like arguments to methods; you should never mutate arguments passed to you.
Keeping a component "stateless" means that it doesn't have any state, and all its rendering is based on its props. Of course, there has to be state somewhere or else your app won't do anything! So this guideline is basically saying to keep as many components as possible stateless, and only manage the state in as few top-level components as possible.
Keeping components stateless makes them easier to understand, reuse, and test.
See A brief interlude: props vs state in the React docs for more information.
Use state when you know the variable value is going to affect the view. This is particularly critical in react, because whenever the state variable changes there is a rerender(though this is optimized with the virtual DOM, you should minimize it if you can), but not when a prop is changed (You can force this, but not really needed).
You can use props for holding all other variables, which you think can be passed into the component during the component creation.
If you have want to make a multi-select dropdown called MyDropdown for example
state = {
show: true,
selected:[],
suggestions:this.props.suggestionArr.filter((i)=>{
return this.state.suggestions.indexOf(i)<0;
})
}
props={
eventNamespace:'mydropdown',
prefix : 'm_',
suggestionArr:[],
onItemSelect:aCallbackFn
}
As you can see, the objects in the state variable are going to affect the view some way or the other.
The objects in the props are mostly objects which should remain the same throughout the component life cycle. So these objects can be callback functions, strings used to namespace events or other holders.
So if you do want to update the component by itself, you need to have to look into how componentWillRecieveProps ,componentWillUpdate, componentDidUpdate and componentShouldUpdate works. More or less, this depends on the requirement and you can use these lifecycle methods to ensure that the rendering is within the component and not in the parent.