Altering react form but input value not editable - javascript

I came from angularjs background, with 2 way binding I don't have to worry much, just declare a submit function, I can get the changed or unchanged value.
I'm stuck with react now. My input is not editable.
class HelloWorldComponent extends React.Component {
constructor(){
super();
this.handleChange = this.handleChange.bind(this)
}
render() {
const person = {"name":"james"};
return (
<input onChange={this.handleChange} type="text" placeholder="name" value={person.name} />
);
}
handleChange(e){
}
}
React.render(
<HelloWorldComponent />, document.getElementById('react_example')
);
http://jsbin.com/huluqifanu/1/edit?js,console,output
Also what to do next? should I set the api data to a state first?

If you do not want to use Controlled Components then you can use Uncontrolled Components.
Specifically, you can use the defaultValue prop instead of value on your input.
As to your second question, you will have to be more clear what you are asking or perhaps better to ask in a separate Q altogether.

Related

Creating controlled input in react

I'm a newbie and learning React with FreeCodeCamp. On this challenge it says:
When you type in the input box, that text is processed by the
handleChange() method, set as the input property in the local state,
and rendered as the value in the input box on the page. The component
state is the single source of truth regarding the input data.
I wrote this solution:
class ControlledInput extends React.Component {
constructor(props) {
super(props);
this.state = {
input: ''
};
// Change code below this line
this.handleChange = this.handleChange.bind(this);
// Change code above this line
}
// Change code below this line
handleChange(event) {
this.setState({
input: event.target.value
})
}
// Change code above this line
render() {
return (
<div>
{ /* Change code below this line */}
<input value={this.state.input} onChange={this.handleChange()} />
{ /* Change code above this line */}
<h4>Controlled Input:</h4>
<p>{this.state.input}</p>
</div>
);
}
};
the console says:
“Build error, open your browser console to learn more.”
Where am I doing wrong? I cannot see my mistake..
The issue is related to how you assign the event handler to onChange.
onChange expects a callback function which will be fired when value of the input is changed. With onChange={this.handleChange()} in your post, you actually assigns undefined to onChange since handleChange function update the component state but it doesn't return anything.
Change it to onChange={this.handleChange} does what you expect it to work. When input is changed, this.handleChange will be called and event object will be passed in as parameter.
Hopefully that helps.
You're calling handleChange instead of passing its reference as the onChange prop.
You likely do not need to run your handleChange method in the onChange prop. So you would have this instead:
onChange={this.handleChange}

Avoid to rerender on every change event in React

I am working on an existing React code that someone else wrote, and facing some performance issues in it. Consider the following code snippet:
//ComponentA.js
class ComponentA extends React.Component {
this.state = { someValue : 'dummy' }
// Other code
// We are using Babel, so class fields are OK
updateVal = e => this.setState({ someValue : e.target.value})
// fetchData makes an ajax call
fetchData = () => { fetch(this.state.someValue) }
render() {
return (
<ComponentB val={this.state.someValue}
updateVal={this.updateVal}
fetchData={this.fetchData}/>
)
}
//ComponentB.js
class ComponentB extends React.Component {
render() {
return (
// Other code
//Input is a component from a library
<Input onChange={(e) => { this.updateValue(e) } }
onBlur={this.props.fetchData} />
value={this.props.val}
)
}
}
Now the problem is that whenever user types in Input, the value gets printed after a few seconds. This is because ComponentA is actually a pretty big component (I know it's bad, but I don't want to refactor it now as it's huge and we don't have much time) and it gets re-rendered every time user types. To avoid this, I can make Input an uncontrolled component and update the ComponentA's someValue onBlur. Another way is to have an initialState in ComponentB which is equal to the val prop. And onChange, this.setState is called for ComponentB only. Then onBlur, I can update ComponentA's this.state.someValue.
However, in both these approaches, the principle of single source of truth of React is lost. So what will be the best solution in this case?
Here I would also like to ask, what's the harm in using Uncontrolled Component here?
A quickfix could be to debounce the onChange handler.
I usually use debounce from Lodash but you can use another one, or write your own version.
import { debounce } from 'lodash'
class ComponentB extends React.Component {
// componentA will rerender only every 300ms instead of every time user types
handleChange = debounce(e => this.props.updateVal(e), 300)
render() {
return <Input onChange={this.handleChange} value={this.props.val} />
}
}
The best solution would be to split/refactor componentA though.
The re-rendering is optimized (I don't think that the problem is the related to the component size).
I think that the issue is not caused by your structure (That is the one adviced by React itself) but from the Input component taken from the library.
You could also keep the state in the ComponentB and expose function to get/set values but then, as you said, you will lose the single source of trouth but you will avoid re-rendering every input change.
Hope this will help!

React context props drilling, what is it that I don't get?

There is a TL;DR at the bottom.
I am probably doing this wrong or using the context in an bad way. I am new to react so I have no clue if this is how we are meant to do things.
My understanding:
Context can be used to pass down props to deeper nested child components without having to pass them through all levels of nesting. A provider is filled with props, and a consumer will look "up the tree" to find the nearest provider and get it's data.
If this is the case, then I can load a provider with a function, such as an onChange handler in order to avoid having to write the handler on every child component when they all do the same thing. This would allow for a "smart form" which govern's its input's handlers by "passing" handlers given to it. Obviously just writing one handler on multiple components is not an issue, but having like 20-30 form fields and writing 4+ handlers on each of them just creates code clutter. So I tried the following:
HTML structure is like this, for example:
<ControlledForm data={some_data} handlers={some_handlers}>
<LabeledControl name="Type your name" rel="Name" meta={{some_meta_object}}></LabeledControl>
<LabeledControl name="Pet name" rel="PetName" meta={{some_meta_object}}></LabeledControl>
<LabeledControl name="Type of pet" rel="PetType" meta={{some_meta_object}}></LabeledControl>
<LabeledControl name="Family" rel="Family" meta={{some_meta_object}}></LabeledControl>
</ControlledForm>
And this is the ControlledForm class code:
const { Provider } = React.createContext(); //Note this
class ControlledForm extends Component {
state = {};
render() {
return (
<Provider value={{ onChange: this.props.onChange }}>
<form>{this.props.children}</form>
</Provider>
);
}
}
Now whatever child I place within this form would want to have a <Consumer> wrapper around it to consume the changeHandler, or at least this is the plan. However when I wrap my LabeledControl in a consumer, it acts as if it has no data.
<LabeledControl> (reduced code):
const { Consumer } = React.createContext();
class LabeledControl extends Component {
state = {};
render() {
return (
<Consumer>
{r => {
console.log("consumer:", r); //Logs undefined
return (
<div className="labeled-control">
{/*Code here*/}
</div>
);
}}
</Consumer>
);
}
}
If I was to guess at what the issue is, I'd say it is because both the ControlledForm and the LabeledControl create it's own context, which is not shared, look at the code above. But I do not understand how would I share this context and still keep the two classes in separate .js files. I cannot pass a reference down to the children, all I get is the {this.props.children} and no way to tell it "Hey use this provider here". All the examples I find online have the two classes that are a provider and a consumer in a same file, being able to reference the same "context" but this seriously impacts the freedom of what I can put inside a form, or rather doesn't let me have customization in terms of "children".
TLDR
How do I pass down a "Context" from a Provider to a Consumer when they are in two different javascript files? Code is above. I essentially need to pass down a handler to every child and have it (maybe, maybe not, depending on a child) use the handler to tell the parent to update it's data. All of this whilst using {this.props.children} in a parent in order to allow "outter code" to "inject" the parent component with any children desired and have them either use or not use the parent's handler.
Edit:
I searched about a bit and found two possible solutions, which I both tested and both seem to be working (with a limited use case). Both render props and React.CloneElement seem to do the trick when there is one level of nesting as we can directly render and add props to children with them, but when we need to prop drill several levels, all the components in between would have to implement the same passing of props which then turns to spaghetti code. Still searching to try and find the way to pass the context down to the children for consumption in different files.
Please view the code below.
Also: here is a sample project I have built:https://codesandbox.io/s/5z62q8qnox
import React from 'react'
import PropTypes from 'prop-types';
import 'bootstrap/dist/css/bootstrap.min.css';
export default class ControllerForm extends React.Component {
static childContextTypes = {
onChange: PropTypes.func.isRequired
}
getChildContext() {
return {
onChange: this.handleOnChange
}
}
handleOnChange = (e) => {
console.log(e.target.value) //here is the place you have to implement
}
render() {
return (
<div class="container">
{this.props.children}
</div>
)
}
}
import React from 'react'
import PropTypes from 'prop-types';
import 'bootstrap/dist/css/bootstrap.min.css';
export default class LabeledControl extends React.Component {
static contextTypes ={
onChange : PropTypes.func.isRequired
}
render() {
return (
<div>
<div className="form-group">
<input className="form-control" type="text" onChange={this.context.onChange} />
</div>
</div>
)
}
}
function App() {
return (
<div className="App">
<ControllerForm>
<LabeledControl />
<LabeledControl />
</ControllerForm>
</div>
);
}
It appears that Context is not what I should be using for this, instead either render props or React.cloneElement() is the proper solution, despite my best efforts to enforce a context.
Parent's render:
{this.props.children.map((child, index) =>
React.cloneElement(child, { key: index, handler: handler })
)}
Child's render:
return (
<div>
<span onClick={this.props.handler}>{passed.foo}</span>
</div>
);
This way the structure remains clean and handlers get passed down. Only issue is every component that needs to pass them down has to implement this, but it would have been the same with context, since it is not exported to a separate file.

What's the correct way of accessing input fields in React-Redux?

So I have this application which uses Redux to store some data. Then I have the Search component, which I originally wanted to be a plain dumb component. However, there is an input field whose value I would like to access and pass on to do something with it. However, I am not sure how to manage the value of this input field, that is, how to access it & where to store the value. I want to use Redux and be consistent, so that's my main problem. I have found the following solution (which works), but this does not look like Redux to me anymore? Am I violating any Redux specific rules with this?
On the other hand, how would I do it with Redux? With a reducer & action etc. just for this one input field in one component? This seems like a bit too much, but please enlighten me!
class Search extends React.Component{
constructor(props) {
super(props);
this.state = {
username: ""
};
this.handleUsernameChange = this.handleUsernameChange.bind(this);
}
handleUsernameChange(evt) {
console.log("Helo" + evt.target.value);
this.setState({
username: evt.target.value
});
}
onSubmit(e) {
e.preventDefault();
console.log("Hello" + e);
/* Do something... */
}
render() {
// Return JSX via render()
return (
<div className="">
<h1>Youtube Link</h1>
<input className="form-control" onChange={this.handleUsernameChange}></input>
<button className="btn btn-large btn-positive" onClick={this.onSubmit}>Download</button>
</div>
);
}
}
// Export Search
export default Search
"I want to use Redux and be consistent, so that's my main problem."
That's a valid reason to use Redux for this use case. But it is also fine to use combination of Redux and local component state in your application. I think this is a perfect example on when local component state is a good solution. I wrote a blog post on this topic. If you want, you can take a look here: http://blog.jakoblind.no/2017/02/08/is-using-a-mix-of-redux-state-and-react-local-component-state-ok/
On the other hand, how would I do it with Redux? With a reducer & action etc. just for this one input field in one component? This seems like a bit too much, but please enlighten me!
Yes, this is how you would do it with Redux. If you need the the username value anywhere else in your app, then this is the approach you should take.
If you don't want to write your own actions and reducer and everything for it, you could look into redux-form to handle it for you, but it might be a bit overkill if there is not much other input required from the user.
If you only need it when you hit that button right below it, then what you have is fine as you can just raise your action with the value from the components state, e.g,
onSubmit(e) {
e.preventDefault();
console.log("Hello" + e);
/* Do something... */
this.props.downloadTheThing(this.state.username)
}
where downloadTheThing is mapped in your mapDispatchToProps function when connecting the component (assuming you are using react-redux).
It is definitely fine to use local state in Redux.
From the code snippet you shared, you don't even need to use a local state.
constructor(props) {
...
this.username = '';
...
}
onSubmit() {
console.log('Hello ' + this.username);
}
render() {
return (
...
<input type="text" onChange={e => this.username = e.target.value} />
...
);
}
<input className="form-control" onChange={this.handleUsernameChange}></input>
that is your input field. first thing todo when you work on input in react is to set the value and then apply onChange. with value we will assign the state of the input to the state of the component so we will get single source of truth.
since you want to work on redux store, I assume you already have your redux store, reducers and actions set in your application. reducers hold the state and you send message to reducers via dispatch(). You access to dispatch() on this.props.dispatch() once you define connect() (). By default, a connected component receives props.dispatch()
import React from "react"; //anywhere you are using jsx, u should import this
import { connect } from "react-redux";
import setInputAction from "relative path" //actions are the messages that you send to reducers. reducers change the state based on the actions.
//this allows you access to state in Redux-store via the props.
const mapStateToProps = state => {
return {
reduxState: state
};
};
export default connect(mapStateToProps)(Search);
this code is kinda configuration of your component to communicate with the redux-store.
Now let's focus on input field. we have to set the value:
<input
type="text"
value={this.props.reduxState.input}// i assume you have `input:""` in the state. u can name anything you want.
onChange={e => {
this.props.dispatch(setInputAction(e.target.value));//we are sending e.target.value to the reducer via dispatch().
}}
/>
setInputAction is just an object that sent to reducer. Reducers are already defined what to do under certain actions. So you just send the message and reducer will change the state accordingly.

Communicating between nested child components in React.js

It won't take you long to realise that I am probably out of my depth here. I am not only new to React.js but also to ES6 and so please be gentle with your answers...
Here goes.
I am using a component to build a form input:
const Input = (props) => {
return(
<input ... />
)
}
I have a component which I use to construct HTML around any of the basic form elements that I give it.
const InputWrap = (props) => {
return(
<div class="input-wrap" ...>
{children}
</div>
)
}
Which allows me to do something like this...
const Input = (props) => {
return(
<InputWrap>
<input ... />
</InputWrap>
)
}
What I would like to do is to add a character counting component to the mix:
const InputWrap = (props) => {
return(
<div class="input-wrap" ... >
{children} // which is the HTML input
{props.maxValue && <CharCounter />}
</div>
)
}
So here is my problem...
The <CharCounter /> needs to be notified of any changes happening to the <input /> and update it's internal state.
The <input /> and the <CharCounter /> are siblings and children of the <InputWrap /> and so, from what I can gather, I need a method inside <InputWrap /> which ties an onChange of the <input /> and some method that will update the state within the <CharCount /> component.
I am at a loss as to how I go about adding a callback as the <input onChange={doSomething} /> is in the form {children} by the time it comes in contact with the <CharCount /> once inside the <InputWrap />...
Where am I going wrong here? I'm starting to think it was way back at the beginning...
There are two typical ways of communication between siblings:
You use the InputWrapper as an DataContainer
You use a Data Flow library like flux or redux (which is a lot more complex, especially for this case)
For the 1. you need, as you correctly noticed, an onChange handler for the input component, which is a function defined in the Component and which is passed to the input. If your input component is an own component and not the native component you will need to pass the onChange prop to the native input.
The function in the Component takes the input, counts the chars and sets an internal state variable with setState({ charCount: #CountValue#}). And then you can pass the state variable to the CharCount Component with
One Important thing to mention: You are using stateless components and therefore you will need to change your InputWrap to a normal react component
class InputWrap extends React.Component {
...
}
Hope this will give you the right direction.
Another excellent solution that is like redux but has a different architecture and api is https://github.com/mobxjs/mobx-react.
You can use the inject HOC to inject shared state to any react component in your application.

Categories

Resources