I want to set the state of one component from another component using setState method as it seems to involve less coding, however I have come to know that I can't use the this keyword in a static method which has created a problem for me. I would like to know another get around of this problem. help would really appreciated.
First component
class First extends Component {
filterByLocation(loc) {
const filteredData = this.state.passedInfo.filter(({area}) => area === loc);
this.setState({members: filteredData})
}
}
Second component
class Second extend Component {
renderSuggestion() {
<TouchableOpacity
onPress = {()=> this.setState({location}, () => {
First.filterByLocation(this.state.location);
})}>
<Text> {"Click Me"}</Text>
</TouchableOpacity>
}
}
I initially considered this a comment, but really, it's an answer:
One component should never set the state of another component, even if it had access to the component instance (and thus to setState). It certainly can't do so without access to the component instance.
Instead:
Lift state up and have it passed to the component as props, or
Use portals and again pass the state as props (note: not entirely sure portals are supported in React Native, though a search turns up projects providing similar functionlity), or
Use context (which is supported in React Native)
...or possibly any of several other things. In your specific scenario, lifting state up seems like the right thing.
Why not to pass whole this object to your method like:
<TouchableOpacity
onPress = {()=> this.setState({location}, () => {
Home.filterByLocation(this, this.state.location);
})}>
<Text> {"Click Me"}</Text>
</TouchableOpacity>
Filter method:
filterByLocation(context, loc) {
const filteredData = context.state.passedInfo.filter(({area}) => area === loc);
context.setState({members: filteredData})
}
For sure it is not a good practice, it should solve the problem but should not be overused.
Related
In terms of writing components, which would be the preferred way to write below component? Assume that removeCard is outside of shown scope, ie. redux action.
My assumption would be that ComponentCardB would be, as it avoids passing an unnecessary argument which would be in the scope anyway. I imagine in terms of performance in the grand scheme of things, the difference is negligible, just more of a query in regards to best practise.
TIA
const ComponentCardA = (id) => {
const handleRemove = (cardId) => {
removeCard(cardId);
};
<div onClick={() => handleRemove(id)} />;
};
const ComponentCardB = (id) => {
const handleRemove = () => {
removeCard(id);
};
<div onClick={handleRemove} />;
};
With functional components like that, yes, there's no reason for the extra layer of indirection in ComponentCardA vs ComponentCardB.
Slightly tangential, but related: Depending on what you're passing handleRemove to and whether your component has other props or state, you may want to memoize handleRemove via useCallback or useMemo. The reason is that if other props or state change, your component function will get called again and (with your existing code) will create a new handleRemove function and pass that to the child. That means that the child has to be updated or re-rendered. If the change was unrelated to id, that update/rerender is unnecessary.
But if the component just has id and no other props, there's no point, and if it's just passing it to an HTML element (as opposed to React component), there's also probably no point as updating that element's click handler is a very efficient operation.
The second option is better way because using an arrow function in render creates a new function each time the component renders, which may break optimizations based on strict identity comparison.
Also if you don't want to use syntax with props.id you rather create function component with object as parameter:
const Component = ({id}) => { /* ... */ }
Of course using arrow function is also allowed but remember, when you don't have to use them then don't.
I would like to be able to have a component whose rendering function is in another file in order to have a separation between the logic of my component and the rendering.
Naively, I tried to do just one file containing my component and which rendered a functional component of the same name to which I passed the necessary props so that everything was displayed correctly.
Something like that :
// MyComponent.render.jsx
export default MyComponentRender = (props) => {
return {
<View>
// render all components of my view
</View>
}
}
// MyComponent.js
class MyComponent extends Component {
// some logic
render() {
return (
<MyComponentRender
aLotOfProps=....
/>
)
}
}
But I soon found myself having to send, sometimes, a fairly large amount of props and +, I have for example textInputs that need to be focus() or blur() in reaction to some logic in my view but as a result, I couldn't control that just by sending props. It quickly became a mess!
I was wondering if there was a simple way to separate the logic of a component and its rendering function? Maybe there is a way to pass the context of my component to my rendering function/component so that it has direct access to all states and can also store references, etc.?
Thanks you,
Viktor
Newbie here, I am studying the documentation of react and in React Context API, I couldn't understand something, I won't understand the rest of the subject if I don't understand it. Can anyone help me what does it mean through using an example?
The Toolbar component must take an extra "theme" prop
and pass it to the ThemedButton. This can become painful
if every single button in the app needs to know the theme
because it would have to be passed through all components.
class App extends React.Component {
render() {
return <Toolbar theme="dark" />;
}
}
function Toolbar(props) {
// The Toolbar component must take an extra "theme" prop
// and pass it to the ThemedButton. This can become painful
// if every single button in the app needs to know the theme
// because it would have to be passed through all components.
return (
<div>
<ThemedButton theme={props.theme} />
</div>
);
}
class ThemedButton extends React.Component {
render() {
return <Button theme={this.props.theme} />;
}
}
The Toolbar component must take an extra "theme" prop
this can be like <Toolbar theme="dark">
and pass it to the ThemedButton
how Toolbar component pass this prop to ThemedButton? and kindly clarify the rest of the comment as well.
Thank you for any help? You are kind
In your Toolbar component, it takes a parameter props, props is whatever properties have been passed to it when calling it, as in <Toolbar param1="someString" param2={someVariable}>, in this case the props value in Toolbar will be an object with the data you passed as key=value like for example: {param1: "someString", param2: content_of_someVariable}
And if you don't actually use those props (properties)/parameters in Toolbar, but rather in a subcomponent, then you have to pass them again to another level, like in <ThemedButton theme={props.theme} />, then ThemedButton itself finally passes the value to the component that actually makes use of, which is in your case: <Button theme={this.props.theme} />;.
So you had to pass the theme across multiple components, which don't use it or care at all about it, just to get it through to the final Button component.
(answer ends here, below is my effort to explain context API in an easy way)
To avoid that annoying level to level to another..., you can use the context API. Because it is really incontinent to pass a value across 3-4+ levels/components every time you want to use it in the last one in the chain.
Think about the context like a variable defined and exported on a root level and holds some data (like the user login status for example, or the theme infomation), and whenever you require that data, you import it and use it directly. You use the Provider property of the context you define (MyContext.Provider) to assign the data to it, and you use the Consumer property (MyContext.Consumer) to consume/access that data you assigned in the provider.
The beauty of the context consumer, is that whenever the data is updated in the provider, the consumer immediately gets the new data and triggers a re-render with the new data.
I hope I explained it in a simple and clear way. Write a comment with any questions or unclear parts and I can try my best to improve the answer.
Best of luck!
Props are properties that help define the way your JSX appears on the page.
When you use a component that you have created, you can pass it props like below:
<MyComponent myProp={myPropValue} />
You can continue to pass props down through the component hierarchy as well. So say you have a component tree like below:
MyComponent
--MySubComponent
----MySubSubComponent
You can pass props from MyComponent to MySubSubComponent like so:
<MyComponent myProps={MyProps} />
<MySubComponent mySubProps={props.myProps} /> //Props are the value you gave in the parent component
<MySubSubComponent mySubSubProps={props.mySubProps} />
Whatever title you give the props when declaring the component in JSX is the title you will call to get the value of the prop like props.myProps
I don't want to have a handle refresh function in every single screen in my project, so I created a Helper.js to handle this. This function has this.setState and another call for a function inside the screen component. This is what I got so far but it returns an error.
Exported function
export function handleRefresh(component) {
const {page, refreshing} = component.state
component.setState(
{
page:1,
refreshing:true
},
() => {
component.makeRemoteRequest();
}
);
};
and I call it in the component like this:
<FlatList
...
onRefresh={()=> handleRefresh(this)}
refreshing={this.state.refreshing}
...
/>
I saw that you can pass "this" as a param, but the error still says it is undefined.
setState shall be within that screen always where you are using FlatList because after making API you have to update and control the state of that screen.
All the states will be in the component where FlatList using.
Use case are not logical in my view. You can try to create a helper function which accepts different functions params like: page, isRefreshing and return the API response and also the API url and datapost will also be dynamic. Because you want to use it in many areas. It will be difficult to maintain.
So, If you like then use redux what you want.
https://snack.expo.io/#prashen/flatlist-onrefresh
You can do in this way.
class AComponent {
...
render() {
const thisComponent = this;
<FlatList
...
onRefresh={()=> handleRefresh(thisComponent)}
refreshing={this.state.refreshing}
...
/>
}
};
All this. uses refer a same class or function. Only use IF i'ts a child function, component or method.
I'ts don't work out of class function original, you can make a bridge for share data or status.
You can use redux for it and using stores for update screen state.
I set all the context of my main component from my child component and it works fine, but I don't know if this is correct
This is my main component
import Child from "./apps/child";
export default class NewTest extends Component {
constructor(){
super();
this.state={
one:1,
}
}
render() {
return (
<View style={styles.container}>
<Text>{this.state.one}</Text>
<Child root={this}/> //<----- Here i set all the context of my main Component to the child component
<TouchableOpacity onPress={()=>console.log(this.state.one)}>
<Text>Touch</Text>
</TouchableOpacity>
</View>
);
}
}
and this is my child component
export default class Child extends Component{
constructor(props){
super(props);
this.parent=this.props.root;
}
render(){
return(
<View>
<TouchableOpacity onPress={()=>{
this.parent.setState({one:this.parent.state.one+1}) // <- if you see here i change the state of the parent, and it work fine
}}>
<Text>Sum to Parent</Text>
</TouchableOpacity>
</View>
)
}
}
All this works, but is this the correct way to do it?
No, it's not. If you want to change the state of the parent component you should send a callback function as a prop to the child and then invoke it. For example, you could have a function in your NewTest:
increaseOne(){
this.setState({one:this.state.one+1})
}
Then, send it to the child with a prop:
<Child increaseOne={this.increaseOne.bind(this)}/>
Finally invoke it in the child onPress:
<TouchableOpacity onPress={this.props.increaseOne}>
If the application gets too complex, you could use Redux.
It is not the best "React way" to do it. It would have been better to pass a function to the child component that should work as a callback (something like "sumToParent"); and the parent would react on it by making the sum.
Another option could be using react-router and react-redux to maintain the state.
What you are trying to do, is to get full control over over component. This is not the best way to solve the problem, and the main reasoning that it will sooner or later strike you back. The idea is to pass hanlders via props, so they will be just invoked, but without any knowledge how they work.
So, in the code (function has to be bound already):
<Child increase={this.increase} decrease={this.decrease} />
With this approach you'll get much more easier to maintain and refactor solution – for instance, form can invoke passed submit function, which in the beginning can just fake it through setTimeout, but later will be replaced with real call.
It also increases testability, looses coupling and leads to the better code in general.