So, I'm trying to learn some react, so far egghead.io is pretty good, but I have a question. I have the following code:
https://jsfiddle.net/42pe/69z2wepo/49393/
Basically these are 3 sliders which update the state on the parent component. Pretty straightforward.
Specifically, I can update the state like this (by passing both val and color):
updateMe(val, color) {
let obj = {};
obj[color] = val;
this.setState(obj);
}
Or I could use the Slider ref to findDOMNode() and then get the value to update the state, but that just didn't feel right. I have no clue what React does in the back, but calling a function from an instance, just so that function can find the instance again to get it's value, when the instance itself could be passing it's value (both value and color prop) all along seems weird.
So, is this a bad practice for some reason?
Both methods are correct but prefer the updateMe . Passing functions in props and child calling that prop function is the ideal way in React.
Refs are ideal where you just want the value and dont want to update state of the react component .For eg , in forms you can use ref to get all the values and make a ajax call to the server.
A better and much cleaner way to do this using ES6 is:
updateMe(val, color) {
this.setState({
[color]: val
});
}
findDOMNode is not a bad practice. But you do not need use it if, you build your application as a function of state and props.
Any change to your state or props should change your DOM and any change to your DOM should be as a result of function of change in state or props.
Related
Say I have a simple component like this one:
export default function Foo({someProp}) {
const a = Math.random();
return <div>{a}{someProp}</div>
}
As far as I know, when someProp updates, React will trigger a re-render. Will it execute the whole Foo function once again and reassign const a a new random value? Will the value be displayed in the <div>?
Thank you.
The answer for every one of your question is yes. A re-render is triggered when there is a props change as you said, and also when there is a state change. When re-rendering and also on the first render, everything behaves like in a normal JavaScript function, as far as assigning variables and everything else, except for some things related to React Hooks, like a state made with useState, a ref made with useRef...
function App() {
const [currentPanel, setCurrentPanel] = useState(ProfilePanel); // ProfilePanel is a component
return (
<div className={styles.App}>
{currentPanel}
</div>
);
}
In code i set the component "ProfilePanel" to a "curentPanlel" state, then in App i change the component in state, and this render an another panel. the problem is that i dont know how to pass props when i render it like this.
i tried the {currentPanel()} but is return an error.
please help to find a method to solve this, or if this method to render a component in state are absolutly wrong tell how to do this another way.
the problem is that i dont know how to pass props when i render it like this
You'd do it by using an initial capital letter for the state member (CurrentPanel instead of currentPanel), and then using it as normal (<CurrentPanel someProp="some value" />). (It has to be initially-capped because that's how JSX knows it's supposed to be a component, not a tag name.) But, you'll struggle to set a different component function in state, because component functions are, well, functions, and when you pass a function to a state setter, it thinks you're using the callback version of the state setter and calls your function, rather than setting it in state.
If you absolutely have to hold a component function in state, wrap it in an object, but it's much more likely that there's a better solution to the overall problem you're trying to solve.
Assume that we are going to make a text edior.
When typing, Component C(Text input section) needs the state of Component B(Font stlye picker) to determine what style to use.
We can share that state by Vuex, but in OOP, related logic or variables are better to be put in same class, and when other components need the state of that object, just to refer it directly.
If I want to refer components directly, one way is to save some components when they are mounted, and pass them as props to other components as the following.
// in <template>
<FontStylePickerVue ref="stylePicker" />
<TextInputSectionVue
ref="textInputSection"
v-bind="{
tylePickerComp: stylePickerComp
}" />
//in Main Component <script>
mounted(){
this.tylePickerComp = this.$refs.stylePicker;
}
data(){
return { tylePickerComp : null }
}
In Vue, although there are many ways to communicate between components, I never seen any of them passes component instance as props directly like this.
(it works, and very convinient though.)
I know doing like this, in a way, it make system more complicated because components refer each others in a complex relation, but I'm just wondering if there is any design pattern like this in Vue, or doing like this has some cons like low perfermance ,etc.
In the end, in order to clarify the idea, I want to compare what if I pass component as props directly or use common methods in this example:
Pass component as props directly:
Need to save component reference on mounted, but type declartion is once and for all(ex. use InstanceType). Need to deal with null(it's null before mounted).
Event bus:
Emit an event with a promise resolve function as callback to make other components to resovle with their state. This is good because it will keep data or logic in the same class(compare to use Vuex), but you need to write your method in a promise style in order to read resolved result.
Vuex:
Split some variables into global scope, and this is not so compatible for cases which are better to gather logic and data at same place.
Just want to get some idea for this topic, sorry for long article.
I am attempting to assign an object that is passed into a React.Component as a property to a state value like so,
state = {
rota: this.props.rota
}
render() {
// const { cleaning, chairs, creche, flowers } = this.state.rota;
const { cleaning, chairs, creche, flowers } = this.props.rota;
console.log(creche);
}
The commented out section where it get's the value of the state prints out an empty string, however the property values are correct. Am I doing something wrong when assigning the props.rota to the state.rota?
I am using typescript and have done exactly this in another place in my program - however the property being passed in was a value type (string) rather than an object type.
Thanks!
That's usually a bad practice.
Take your case.
You get a value as prop from a parent component.
The inner component will already be re rendered every time that this value changes in the parent component.
If you go with an approach like yours, what most probably you will do, is to change that value inside the inner component too (through state), whose changes will not be reflected on the parent component.
You are actually breaking the design pattern of uni directional data flow, on which react relies a lot on.
So my personal opinion is to lift up the state in this case and avoid such kind of situations. Use callbacks instead if you want to communicate changes to the parent, or use some state management (context, redux, etc..).
Or design a better solution using HOC or render props Components.
Even though #quirimmo has pretty much answered your question, if you want to do this sometime in the future, the easiest way would be to use a constructor function and pass the props in as a param, and then just set that as the default value of the state
class SomeComponent extends Component {
constructor(props){
super(props);
this.state = {
rota: props.rota,
}
}
}
This makes sure that the prop is actually available in the moment you want to set the initial state, since the constructor is the first function that is called in the component lifecycle.
I have a global data object I update and then I call React.renderComponent() again on the top/main component.
Is this the right pattern for triggering this update?
You should generally pass the data object into the component as a prop, even if it's a global variable. This lets you test the component, and also use it elsewhere without being tied to that global.
As Mike said, there's nothing wrong with using React.renderComponent to update it.
He also mentioned flux, but that's overkill for this. A simple event emitter where you do something like .emit('change', newData), and the component is listening for the change event tends to be better for simpler cases. See my answer to this question for an example of how that can be done.
This is the correct pattern. React.renderComponent will either mount a component for the first time, or get an already mounted component to update.
If you're using a global object though, you might want to look in to the Flux architecture here:
http://facebook.github.io/flux/docs/overview.html
I had the same problem and asked myself if I really needed to re-render the component.
You can do so with this.forceUpdate() but it's not advisable. As React docs states:
You should try to avoid all uses of forceUpdate() and only read from this.props and this.state in render(). This makes your component "pure" and your application much simpler and more efficient.
So what I did was create a data property like exists and test it:
// renderDeleteButton() is being called on render()
renderDeleteButton () {
if (!this.props.store.exists) {
return;
}
return(
<DeleteButton
...
deleteAction={this.delete} />
);
}
Whenever I delete/save, I toggle exists and component will show up or hide based on that. React handles that for me.