React Native - Handle parent state from child component (custom component) without add function in parent - react-native

I have read this answer, but I want to change the structur of code a little bit,
actually I want to set parent state from child component but i don't want to add any function in parent component
actually the expected result looks like :
class Parent extends React.Component {
constructor(props) {
super(props)
this.state={modalVisible:false}
}
render() {
return (
<Child modalVisible={this.state.modalVisible} />
<Button onClick={()=>this.setState({modalVisible:true})/>
)
}
}
class Child extends React.Component {
handler(e) {
//handle parent moadlVisible state to false again
}
render() {
return
<Modal
modalVisible = {this.props.modalVisible}>
<Button title="Close Modal" onPress={()=>this.handler(e)}/>
</Modal>
}
}
so I want to make it easy to call the child component without add some function in parent to handle the child component itself, even for closing the modal of child component
Is there a way to achieve what I want?

If you don't want any connection between the 2 components at all, then you may have to use a global state store, such as redux.
Docs can be found here:
https://redux.js.org/introduction
Redux creates a global state instead of local state between all of the components. It does require a little configuration but once you have it fully integrated, you can handle your scenario. Also as your components grow, it'll be easier to keep track of state.
Why can't I just use events?
Quote in a question here..
react.js custom events for communicating with parent nodes
The React way would be to pass callbacks down to children explicitly
via props — . There's no support for custom events w/ bubbling in
React.
The reactive programming abstraction is orthogonal:
Programming interactive systems by means of the observer pattern is
hard and error-prone yet is still the implementation standard in many
production environments. We present an approach to gradually deprecate
observers in favor of reactive programming abstractions. Several
library layers help programmers to smoothly migrate existing code from
callbacks to a more declarative programming model.
A horrid way to do it in my eyes would possibly be to use Async Storage
to store the key value, but I'm not going to suggest that.

Related

Change state from other component (without passing setState function)

I have a quite decent background in android but now I am starting digging into react native and I am really confused with the ways to change the state of a component through hooks and set state function.
To the point, I have my main screen and I have many small components which change visibility. This is done by letting the user change some filter settings within dialogs. So the suggested way to do that is by having a hook in my main screen with a list that holds the values for the visibility of each component. But since I change the visibility of the components from inside the modals, every time I want to show a modal I will have to pass in a different function(for example setComponentEnable or setComponentDisabled) to set the state for each component. So my main screen will be polluted from all these small functions. Also I should not forget to mention that my modals are consisted from many smaller components and I will have to pass as deep as it goes the proper function to match the user action.
So my question is, is there a way to do this thing without polluting my main with all these small functions and make it possible for the main screen to update every time the user change the filters within the modals?
I already read about context but the docs say:
Context is designed to share data that can be considered “global” for a tree of React components, such as the current authenticated user, theme, or preferred language.
So I dont think that this should be a great case for context use.
What I am trying to do now is create a hook with a list
const [isibility, setVisibility] = useState([]);
create visibility handler functions
const setVisibilityEnable = () => {
...
}
and pass it into my modal.
<MyModal
visibilityHandler={setVisibilityEnable}/>
Is there a way to manipulate the state without passing all these callbacks to the modals? Or maybe is there anyone that can suggest a better and clean solution to avoid end up having a really huge main screen?
you can include all the settings in one object and pass that object to all the components. Then each component will then modify that object accordingly.
const defaultVisibility = {
childComponentOne: true,
childComponentTwo: true,
};
const [visibilityObject, setVisibilityObject] = useState(defaultVisibility);
pass both the function and the object into your child components:
<ChildComponentOne visibilityObject={visibilityObject} setVisibilityObject={setVisibilityObject} />
Then in your child component, you set the visibility like so:
setVisibilityObject({...visibilityObject, childComponentOne: false});
Why you don't just pass a property to your modal and check if changed in oncomponentdidchange method?
componentDidUpdate(prevProps) {
if (this.props.yourPoperty!== prevProps.yourPoperty) {
//do your visibility stuff
}
}
Alternatively you can do it with redux when you connect your components to the store.

Any way to make a React presentational component react to MobX store changes

I have a React table component that gets its data via a prop called TableStore. This prop is a high-level abstraction for getting row data:
interface TableStore<RowType> {
getRowIds: () => Array<RowId>;
getRow: (rowId: RowId) => RowType | undefined;
}
interface MaterialTableProps<RowType> {
tableStore: TableStore<RowType>;
}
function MaterialTable<RowType>(props: MaterialTableProps<RowType>) {
...
}
As you can see MaterialTable is not a MobX observer. It is part of a component library that is not dependent on MobX.
When I use this component in my app, I supply it a MobX-based TableStore. I would like the table component to re-render whenever the MobX-based store changes:
<MaterialTable tableStore={orderStore} />
However that does not happen because the table component is not a MobX observer. Is there any way to force the table component to re-render? For example, I am able to force a re-render by dereferencing the store in the parent component (using a simple console.log()). But this feels like a hack. Is there a better way?
Answering my own question....
I looked at several options but all of them were kludgy. I finally decided to rework the props of the table component to pass in an array instead of an abstract TableStore interface (which the table component can't react to). This allowed me to refrain from adding MobX as a dependency to the table component library while still leverage MobX in the parent component. In summary, the parent component now watches the MobX store, reacts to changes by creating a new array and passing it to the table component.
Here's the new interface for the table component:
export interface MaterialTableProps<T extends Entity> extends TableProps {
entityList: Array<T>;
}
export function MaterialTable<T extends Entity>(props: MaterialTableProps<T>) {
...
}

Large components as sections in VirtualizedList/etc?

If I want to display a bunch of heterogenous data in a virtualized list, it seems like the default way to do it is have the parent component gather up all the data so that it can create the sections to supply to the list component.
Is there any way to avoid requiring the parent component from doing this? I'd like to decouple the parent component from the data gather part, so that all it has to do is declare it has such and such components, and then those components would be responsible for gathering the data.
This would be exceedingly simple if it were a ScrollView:
<ScrollView>
<SectionA>
<SectionB>
<SectionC>
</ScrollView>
However, to leverage the performance gains of a VirtualizedList, if each section is large, I would need to pass in the individual data of each section into the VirtualizedList. I'm not sure how to do this or if it's possible.
Not sure if this is idiomatic or a gross React anti-pattern, but the way I solved it was to implement each section as purely headless data Component.
export type SectionDataComponentProps = {
onDataChanged?: () => void, // call this whenever the data updates.
}
export class SectionDataComponent<P : SectionDataComponentProps, S, ItemT> extends React.PureComponent<P, S> {
// Implemented by subclasses
getSectionData() : Array<SectionT<ItemT>> {
// returns an array of sections for a SectionList...
}
render() {
// business logic component only.
return null;
}
}
The parent component keeps track of them through the use of ref, and then calls getSectionData() as needed.

Changing a property of an object in child component using VueJs

I have a simple question but I can't find the answer to it.
This code (that belongs to a child component? is a good practice?
export default {
name: 'Wish',
props: ['wish'],
data () {
return {
isEditing: false
}
},
methods: {
completeWish () {
this.wish.done = true
},
...
Or I should update the done property of wish object in the parent using emit?
Thanks
As Amade mentioned is a bad practice.
You should use events in order to accomplish it properly from a design point of view.
<script>
export default {
name: 'Wish',
props: ['wish'],
methods: {
completeWish () {
this.$emit('wish-is-completed')
}
}
}
</script>
And then in parent you have v-on:wish-is-completed="handleCompletedWish" like:
// Parent template
<template>
<wish v-on:wish-is-completed="handleCompletedWish"></wish>
</template>
EDIT:
I understand the answer was downvoted because you actually are able to mutate properties of props (not a direct prop reference) and don't get a warn when you do that.
Does it mean you should do that?
No.
Props are created for one-directional data flow purpose. When you mutate props to notify a parent of something, it quickly leads to having hard to maintain state changes, because they are not implicit. Use events for child->parent communication as official documentation suggest in such cases.
All props form a one-way-down binding between the child property and
the parent one: when the parent property updates, it will flow down to
the child, but not the other way around. This prevents child
components from accidentally mutating the parent’s state, which can
make your app’s data flow harder to understand.
Vue docs advise against mutating the props in the child component:
All props form a one-way-down binding between the child property and the parent one: when the parent property updates, it will flow down to the child, but not the other way around. This prevents child components from accidentally mutating the parent’s state, which can make your app’s data flow harder to understand.
In addition, every time the parent component is updated, all props in the child component will be refreshed with the latest value. This means you should not attempt to mutate a prop inside a child component. If you do, Vue will warn you in the console.
So based on this I would tend to say it's a bad practice.
documentation:
Due to the limitations of modern JavaScript (and the abandonment of Object.observe), Vue cannot detect property addition or deletion. Since Vue performs the getter/setter conversion process during instance initialization, a property must be present in the data object in order for Vue to convert it and make it reactive
if you want add or change property use Vue.set like this:
this.$set(this.wish, 'done', true)
see example App:
https://3xyx386q65.codesandbox.io/setProps
see example code (open components/setProps/childComponent.vue):
https://codesandbox.io/s/3xyx386q65
note! wish init as Object! See components/setProps/setProps.vue in example code

State Management with Multiple Inputs (Lists)

I'm trying to figure out the best place to manage the state for lists of input in react-native but haven't found any good, thorough examples or clear guidance and I can see a few options.
For simplicity not including specifics about the tool for managing state, as my understanding is how the state is stored doesn't impact the component where it's managed.
Scenario
A screen component that receives an array of items as props to be displayed in a List of ListItems. Each ListItem includes a input, for simplicity imagine a switch (boolean).
Use cases include an array of form questions or settings to be displayed in a list and allowing user input. Pseudocode:
class SettingsView extends Component {
render () {
return (
<View>
<List style={styles.list}>
{this.props.inputArray.map((item, index) => (
<ListItem
title={item.title}
isSwitched={item.value}
key={index}
onSwitchChange = {this.onChange}
/>
))}
</List>
</View>
);
}
}
Option 1
Based on the Thinking in React page, one option that comes to mind is managing state at the Screen (SettingsView) level by creating an array of (inputArray).length in the SettingsView constructor state and have the onChange function update that array based on key.
Option 2
The second option I see is having each ListItem manage the state it's displaying. This makes sense from an encapsulation perspective to me, but then less so for managing of the state, given that the onSwitchChange function is in the SettingsView and I'm not as clear how this would work.
Are there other options I'm not considering? Admit that experience in React/RN is limited and def coming from a more object mindset like iOS's list datasource model.
Note: Another option is having the entire inputArray in state, instead of passed as props. My understanding is that state should be minimized, so was designing that only the inputs to each item in inputArray should be in the state. i.e. Form Labels (i.e. questions) are props not state.
Option 1 would be the better choice, there is this concept "Smart Components and Dumb Components"
Smart Components: typically holds the state of all the child components associated with it, it also defines the functions that is passed down to child components to modify its state.
Dumb Components: These are components that receives props which includes data and functions they typically don't have their own state and relies heavily on the parent component.
The problem is that when you create a component you need to decide whether it's smart or dumb, usually I associate a screen to a smart component, in your example it would be the SettingsView(smart) that will hold the state and function and it's children will be the dumb components but this is really application specific decision because you might have a SettingsView that are dynamic based on context and so it would be much better to make it a dumb component let's use your example above
Since Settings View relies on this.props.inputArray passed from a parent
component(I will call this ParentComponent) you couldn't modify
inputArray directly in SettingsView what you could do is pass another prop from
ParentComponent to SettingsView which is a function that modifies inputArray
class ParentComponent extends Component {
constructor(props) {
super(props);
this.state = {
inputArray: [],
};
this.onSwitchChange = this.onSwitchChange.bind(this); // when passing fn
}
onSwitchChange(index) { // index will come from the child component
// do some logic here based on the index then update the state
this.setState('inputArray' updatedState); // updatedState is just an example variable
}
render() {
return (
<View>
<SettingsView
onSwitchChange={(index) => () => this.onSwitchChange(index)}
inputArray={this.state.inputArray}
/>
</View>
);
}
/*
(index) => () => this.onSwitchChange(index)
this is a function that will return another function
we need this because we want to delay the execution of onSwitchChange
and capture index and associate it with that method, typically this
is used if were passing function to child component(SettingsView) which will be used as
a handler for events.
*/
}
class SettingsView extends Component {
render () {
return (
<View>
<List style={styles.list}>
{this.props.inputArray.map((item, index) => (
<ListItem
title={item.title}
isSwitched={item.value}
key={index}
onSwitchChange={this.props.onSwitchChange}
/>
))}
</List>
</View>
);
}
}
This example might be pointless because you could just use SettingsView as the parent of ListItem and other components but since SettingsView state is now managed by ParentComponent it is now a dumb component and can be used in other screens that have the specific state that SettingsView needs to operate. the general goal is to create as many dumb components as possible the reason being is that these type of components are highly reusable because you just need to pass them props to properly work.