how to replace this v-model pattern in react hooks - javascript

Migrating my vue application into react. In vue 2, using a v-model on a component was an equivalent of passing a value prop and emitting an input event.
If we wanted to change prop or event names to something different, we would need to add a model option to ChildComponent component.
Here is my vue child component:
export default {
model: {
prop: 'checked',
event: 'change'
},
props: {
value: String,
checked: {
type: [Boolean, String, Array],
},
}
}
In Parent component i am using like this.
<ChildComponent :checked="SelectedFiles" />
I want to know how to handle this pattern in react hooks. I am not that much familiar with react. Any guidance will be helpful for me.

You could use useState react hook.
For example:
export const MyComponent = () => {
const [value, setValue] = useState('');
return (
<input
type="text"
value={value}
className="form-control"
placeholder="Search..."
onChange={(event) => setValue(event.target.value)}
/>
);
};

Related

Using handleChange inside a default function instead of a class

I would like to use a Dropdown module with Semantic UI and react. The issue is all examples provided online use a default class App extends Component however I want to export default function App(). When I do this the I get a parsing error for the render() section, requiring a semicolon.
The below works very well but how can I implement it if the export was a default function instead?
import React, { useState, Component } from 'react'
import { Dropdown, Grid, Segment } from 'semantic-ui-react'
export default class DropdownExampleControlled extends Component {
state = {}
handleChange = (e, { value }) => this.setState({ value })
render() {
const { value } = this.state
return (
<Dropdown
onChange={this.handleChange}
options={options}
placeholder='Choose an option'
selection
value={value}
/>
)
}
}
Writing const infront of the handleChange did not fix anything, it just made the "value" undefined later on. I am very unsure of how to use this because I am new to JS. Any help is greatly appreciated.
When using function components rather than class components you have to utilise the hooks in React.
Heres how your code would look in a function component
export default const DropdownExampleControlled = () => {
const [yourState, setYourState] = useState({});
// yourState being the name of the state and {} being your initial state
const handleChange = (e, {value}) => {setYourState(value)}
return (
<Dropdown
onChange={handleChange}
options={options}
placeholder='Choose an option'
selection
value={yourState}
/>
)
}
Hooks are the newer way of persisting data, handling re-renders & more in modern React. If you want to learn more about how you can use hooks, here's a link to the docs https://reactjs.org/docs/hooks-intro.html

Set Child Component TextField's Value to Parent Components Dropdown Selection?

In the following two codes, There is a child and a parent component. The child component has a TextField, and the parent component has Dropdown, is there a way I can set the TextField's value to whatever I change the Parent Dropdown to? For example, if I selected "Medium", I want to change the child component TextField's value to "Medium". Is this possible?
Here is the Child Component
import * as React from "react";
import { TextField } from 'office-ui-fabric-react/lib/';
const ChildComponent = (props) => {
return(
<TextField
label="Description" required
key="descriptionKey"
styles={props.styles}
value={props.value}
multiline={props.multiline}
onChange={props.onChange}
defaultValue={props.defaultValue}
placeholder={props.placeholder}
/>
);
}
export default ChildComponent;
Here is the Parent Component
import * as React from "react";
import ChildComponent from './ListItem';
import { Dropdown, IDropdownOption } from 'office-ui-fabric-react/lib/';
class ParentComponent extends React.Component {
handleChange = (event) => {
this.setState({
operationType: event.target.value,
})
};
render(){
const riskAssess: IDropdownOption[] = [
{ key: 'high', text: 'High' },
{ key: 'medium', text: 'Medium' },
{ key: 'low', text: 'Low' },
]
return(
<div>
<Dropdown
label="Risk Assessment" required
ariaLabel="Risk"
styles={{ dropdown: { width: 125 } }}
options={riskAssess}
onChange={this._onChange}
/>
<ChildComponent value="This is what I want to change to Dropdown value" />
</div>
);
}
private _onChange = (event: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void => {
this.setState({operationType: item.text})
console.log(event);
};
}
export default ParentComponent;
Yep! Replace "This is what I want to change to Dropdown value" with {this.state.operationType}. The curly braces tell JSX that you want a variable to populate that value.
With TypeScript, your components need to define their property and state structures, by adding generic arguments to React.Component:
interface ParentProps = {};
interface ParentState = {
operationType: string;
};
class ParentComponent extends React.Component<ParentProps, ParentState> {
For ChildComponent, you'd want to extend React.FC (short for FunctionalComponent) in a similar way. Since it doesn't define any state, it can forego the second generic argument:
interface ChildProps {
styles?: React.CSSProperties;
value?: string;
multiline?: boolean;
onChange?: React.ChangeEventHandler<HTMLInputElement>
defaultValue?: string;
placeholder?: string;
};
const ChildComponent: React.FC<ChildProps> = (props) => {
Now TypeScript knows what to expect.
TypeScript's precise interface definitions ensure that each component gets only what it allows. It replaces React's PropTypes warnings with enforced compilation errors. It's more complex, having to define all this, but it makes for less error-prone development.

Using recompose utility as element

I want to use recompose utility functions as react element, so that I can use them in JSX as hoc.
const enhancedInput = props => {
return (<OnlyUpdateForKeys keys={['name']}>
<Input
id="name"
value={props.name}
onBlur={props.handleNameBlur}
onChange={props.updateName}
type="text"
className="validate"
placeholder="Enter Component Name"
/>
</OnlyUpdateForKeys>)
}
This is what I have tried till now, but it utterly fails.
import { onlyUpdateForKeys } from 'recompose';
export const OnlyUpdateForKeys = ({ keys, children }) => {
return onlyUpdateForKeys([...keys])(children)(children.props);
};
export default OnlyUpdateForKeys;
because children is a symbol and react-element instance and not a class/function.
react.development.js:368 Uncaught TypeError: Cannot set property 'props' of undefined
at Component (react.development.js:368)
at ShouldUpdate (Recompose.esm.js:558)
at OnlyUpdateForKeys (recomposeComponent.js:4)
at mountIndeterminateComponent (react-dom.development.js:14324)
Can somebody guide me?
onlyUpdateForKeys([...keys]) higher-order component expects React component as an argument, while children is React element:
onlyUpdateForKeys([...keys])(children)
It would be workable like:
export const OnlyUpdateForKeys = ({ keys, children }) => {
const EnhancedChild = onlyUpdateForKeys([...keys])(props => children);
return <EnhancedChild/>;
};
But it doesn't make sense because it doesn't prevent child component from being updated. OnlyUpdateForKeys is created on each EnhancedInput render. Input is rendered every time EnhancedInput is rendered too, because children are rendered any way - otherwise they wouldn't be available as props.children.
While onlyUpdateForKeys is supposed to be used as:
const EnhancedInput = onlyUpdateForKeys(['name'])(props => (
<Input ... />
))
It's shorter and more efficient than OnlyUpdateForKeys utility component.

Calling parent component method from child component written in Jsx Vue

I didn't expect it be tough.
I've a child component written like
InputWrapper.vue
<script>
import StyledInput from "./input";
export default {
name: "MyInput",
props: {
multiline: Boolean,
onChange: Function
},
render(h) {
const { multiline, onChange } = this;
console.log(onChange);
return (
<div>
<StyledInput multiline={multiline} onChange={e => onChange(e)} />
</div>
);
}
};
</script>
Then I have actual input as vue-styled-components as Input.js. For those familiar with styled components need not explain that code.
Then I consume this InputWrapper component in my parent component Home.vue
<template>
<div class="pLR15 home">
<Row>
<MyInput :multiline="true" placeholder="Sample Input" type="text" :onChange="handleChange"></MyInput>
</Row>
</div>
</template>
<script>
// # is an alias to /src
import HelloWorld from "#/components/HelloWorld.vue";
import Row from "#/ui_components/row";
import MyInput from "#/ui_components/form/input/index.vue";
export default {
name: "home",
components: {
HelloWorld,
Row,
MyInput
},
methods: {
handleChange: function(e) {
console.log("Hey you are changing my value to", e.target.value);
}
}
};
</script>
Problem - onChange on parent is not fired.
#change.native="handleChange" did the trick for me. Also I cleaned up all the event handlers listening on child. Since my type and placeholder properties were passing down as such, so should be the event listeners. With that thought the .native modifier works best for now. Although things could have been uniform by just #change="handleChange" which I was trying earlier and lead to trying out passing function as props to child.
IMP NOTE - Vuejs doesn't treats #change as normal onChange. #input is preferred if the idea is to capture every keystroke. Here comes the opinions! :)

Setting mobX state from inside a React-Apollo component

When trying to set the onChange state of an input element, I am unable to change the state. React-Apollo components need a child function that returns some JSX. Inside the child function It appears that the thisobject is being inherited, but I cant get it to actually change.
If I remove the <Mutation/> component all together then everything works as expected. Is there something special about React-Apollo components or the way the this object interacts with arrow functions?
import React from 'react';
import gql from 'graphql-tag'
import { Mutation } from 'react-apollo';
import { extendObservable } from 'mobx';
import { observer } from 'mobx-react';
const mutation = gql`here is a mutation`
class Why extends React.Component{
constructor(props) {
super(props);
extendObservable(this, {
text: '',
})
}
onChange = e => {
this.text = e.target.value;
};
render () {
return (
<Mutation mutation={mutation}>
{ () => (
<div>
{console.log(this)}
<input name="text"
placeholder="Enter Text"
type="text"
value={this.text}
onChange={this.onChange}/>
</div>
)
}
</Mutation>
)
}
}
export default observer(Why);
I think the state is actually changing but the component render didn't react for you.
It happens because observer components only tracks data that directly accessed by the render function, in your case you have a function that don't.
a simple solution is to use the <Observer /> component from mobx-react:
render () {
return (
<Mutation mutation={mutation}>
{ () => (
<Observer>
{() => (
<div>
{console.log(this)}
<input name="text"
placeholder="Enter Text"
type="text"
value={this.text}
onChange={this.onChange}/>
</div>
)}
</Observer>
)
}
</Mutation>
)
}

Categories

Resources