React Select with Redux React form - javascript

I am trying to integrate react-select with react redux form (https://github.com/davidkpiano/react-redux-form)
This is my current component set up and I am passing props to it from another component.
...
<MultiSelect model="event.category" options={this.props.categoryList} />
...
Multi select component
import React, {Component} from 'react';
import {Control} from 'react-redux-form';
import Select from 'react-select';
import 'react-select/dist/react-select.css';
class MultiSelect extends Component {
constructor(props) {
super(props);
this.state = {categoryValue: []};
}
handleSelectChange = value => {
this.setState({categoryValue: value});
};
render() {
let reactSelect = props => (
<Select
{...props}
/>
);
return (
<div className="form__row">
<div className="form__label">
<span className="form__title">
{this.props.title}
{this.props.isRequired ? (
<span className="form__required">*</span>
) : (
''
)}
</span>
</div>
<Control.custom
model={this.props.model}
id={this.props.model}
component={reactSelect}
simpleValue
multi
value={this.state.categoryValue}
options={this.props.options}
onChange={this.handleSelectChange}
joinValues
name={this.props.model}
required
/>
</div>
);
}
}
export default MultiSelect;
My problem is that I can't seem to grab the value of that hidden text field in my react redux form state. What could I be missing?
Here is the code sandbox too https://codesandbox.io/s/ww4wqyp02l

From the documentation;
If you do not want any standard property mappings (such as onChange,
onBlur, etc.) passed down to your custom control component, use
and define your own mappings:
<Control.custom
component={SpecialCustomText}
mapProps={{
onTextChange: (props) => props.onChange,
onLoseFocus: (props) => props.onBlur,
// etc.
}}
/>
Also, you needed a submit button on which you can retrieve the value from the MultiSelect component that you've made.
I've made changes to reflect these changes on your codesandbox here

Related

GooglePlacesAutocomplete - passing info to parent component and defining language

In my react sign-up form I have the GooglePlacesAutocomplete component to autocomplete user city and country. I have a question - how do I pass the selected city and country to my parent component's state? Normally I use onChange but it doesn't work. onPress doesn't work either (or I am using it incorrectly).
The code looks something like this:
import React, { Component } from "react";
import GooglePlacesAutocomplete from 'react-google-places-autocomplete';
export default class SignUp extends Component {
constructor() {
super();
this.state = {
cityAndCountry: ''
};
}
render() {
return (
<h2>Place of birth</h2>
<GooglePlacesAutocomplete
apiKey="myApiKey"
autocompletionRequest={{ types: ['(cities)'] }}
apiOptions={{ language: 'en'}}
)
}
}
You can use this prop defined in their official docs.
<GooglePlacesAutocomplete
selectProps={{
value,
onChange: setValue,
}}
/>

Live updates between React components based on state

My (simplified) goal:
Show a form input's text content beside the form itself and update the reflected text as the input text changes. The form lives within a React component, and the displayed text lives inside another one.
I can use the component state to control the input's text and change the state based on onChange form event. But how can I also change the state of the displayed text so that I get the live updates I'm looking for?
Input and output components have the same parent component.
Here's my input component:
import React, { useState } from "react";
function InputBoxTest() {
const [inText, setInText] = useState("");
const handleChange = event => {
setInText(event.target.value);
// My instinct is to setOutText here, but I can't...
};
return (
<textarea className="form-control" id="comment" onChange={handleChange}>
{inText}
</textarea>
);
}
export default InputBoxTest;
My output component:
import React, { useState } from "react";
function OutputBoxTest() {
const [outText, setOutText] = useState("");
return <p>{outText}</p>;
}
export default OutputBoxTest;
And my parent component:
import React from "react";
import InputBoxTest from "./InputBoxTest";
import OutputBoxTest from "./OutputBoxTest";
function Test1(props) {
return (
<>
<div className="row">
<div className="container-fluid col-sm-7">
<InputBoxTest />
</div>
<div className="col-sm-5">
<OutputBoxTest />
</div>
</div>
</>
);
}
export default Test1;
You could move the State Hook from InputBoxText into the ParentComponent Test1
InputBoxText is then used for displaying and updating the state
OutputBoxText is used for displaying only
import React from "react";
import InputBoxTest from "./InputBoxTest";
import OutputBoxTest from "./OutputBoxTest";
function Test1(props) {
const [inText, setInText] = useState("");
const handleChange = event => {
setInText(event.target.value);
};
return (
<>
<div className="row">
<div className="container-fluid col-sm-7">
<InputBoxTest text={inText} handleChange={handleChange} />
</div>
<div className="col-sm-5">
<OutputBoxTest text={inText}/>
</div>
</div>
</>
);
}
export default Test1;
function InputBoxTest(props) {
return (
<textarea className="form-control" id="comment" onChange={props.handleChange}>
{props.text}
</textarea>
);
}
export default InputBoxTest;
function OutputBoxTest(props) {
return <p>{props.text}</p>;
}
export default OutputBoxTest;
If you need to share some state between 2 components, you need to move that state in their (at least first) parent. You can read more about it here.
Basically what this means is that your Test1 component should be holder of your textarea value.
Please see this example based on your code.

How to share state between child component (siblings) in ReactJS?

I would like to pass state to a sibling or even a grandparent whatever.
I have 3 components. Inside Header, I have a button with an onClick function to toggle a Dropdown Menu inside Navigation. And by the way, I would like to pass the same state to AnotherComponent.
How to pass state (such as isDropdownOpened) from Header to Navigation and AnotherComponent?
<div>
<Header />
<Navigation />
<div>
<div>
<div>
<AnotherComponent />
</div>
</div>
</div>
</div>
You have different approaches to address this situation.
Keep the state in the top component and pass it to children through props
Use a state container to keep and share your application state among components (e.g. https://redux.js.org/)
Use the new React Context feature. Context provides a way to pass data through the component tree without having to pass props down manually at every level.
That's the exact reason why "React Hooks" have been developed (and hyped by the community 😉), but don't use them yet in production, they are still in early development (alpha) and their specification/implementation might be changed!
You problem can be solved using the awesome “React Context“ API which allows to pass data to components no matter how deep they are nested in the tree.
To get to know to context read the extensive documentation linked above. I'll only explain a small and quick example here:
Create a context component and export the consumer
App.jsx
import React from "react";
// The initial value can be anything, e.g. primitives, object, function,
// components, whatever...
// Note that this is not required, but prevebents errors and can be used as
// fallback value.
const MyContext = React.createContext("anything");
// This component is the so called "consumer" that'll provide the values passed
// to the context component. This is not necessary, but simplifies the usage and
// hides the underlying implementation.
const MyContextConsumer = MyContext.Consumer;
const someData = { title: "Hello World" };
const App = ({ children }) => (
<MyContext.Provider value={someData}>{children}</MyContext.Provider>
);
export { MyContextConsumer };
export default App;
Import the created consumer in any component and use the provided value
AnotherComponent.jsx
import React from "react";
import { MyContextConsumer } from "./App";
const AnotherComponent = () => (
<div>
<MyContextConsumer>{({ title }) => <h1>{title}</h1>}</MyContextConsumer>
</div>
);
export default AnotherComponent;
Render the app with both context components
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import AnotherComponent from "./AnotherComponent";
const Root = () => (
<App>
<AnotherComponent />
</App>
);
const rootElement = document.getElementById("root");
ReactDOM.render(<Root />, rootElement);
The component will render a level 1 heading with the "Hello World" text.
How to pass state (such as isDropdownOpened) from Header to Navigation and AnotherComponent, please ?
You hold the state in an ancestor of Header and pass that state to Haeader, Navigation, and AnotherComponent as props. See State and Lifecycle and Lifting State Up in the documentation.
Example:
const Header = props => (
<div>
<span>Header: </span>
{props.isDropdownOpened ? "Open" : "Closed"}
</div>
);
const Navigation = props => (
<div>
<span>Navigation: </span>
{props.isDropdownOpened ? "Open" : "Closed"}
</div>
);
const AnotherComponent = props => (
<div>
<span>AnotherComponent: </span>
{props.isDropdownOpened ? "Open" : "Closed"}
</div>
);
class Wrapper extends React.Component {
constructor(props) {
super(props);
this.state = {
isDropdownOpened: false
};
}
componentDidMount() {
setInterval(() => {
this.setState(({isDropdownOpened}) => {
isDropdownOpened = !isDropdownOpened;
return {isDropdownOpened};
});
}, 1200);
}
render() {
const {isDropdownOpened} = this.state;
return (
<div>
<Header isDropdownOpened={isDropdownOpened} />
<Navigation isDropdownOpened={isDropdownOpened} />
<div>
<div>
<div>
<AnotherComponent isDropdownOpened={isDropdownOpened} />
</div>
</div>
</div>
</div>
);
}
}
ReactDOM.render(
<Wrapper />,
document.getElementById("root")
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
There are some other options, which Arnaud usefully provides in his answer.
Like how TJ Said, use the state of the parent component. That way one state is shared by all the sub components, which is what you wanted I presume.
class ExampleParentComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
isDropdownOpened: false
}
}
toggleDropdown() {
this.setState({
isDropdownOpened: !isDropdownOpened
});
}
render() {
return (
<div>
<Header open={isDropdownOpened} toggleDropdown={ this.toggleDropdown }/>
<Navigation open={ isDropdownOpened}/>
<div>
<div>
<div>
<AnotherComponent open={ isDropdownOpened} />
</div>
</div>
</div>
</div>
);
}
class Header extends React.Component {
render() {
return (
<div>
<button onClick={ this.props.toggleDropdown }>TOGGLE ME</button>
{ isDropdownOpened && (
<h1> DROPPED </h1>
}
</div>
);
}
}
You can only use this.state.variableName to access
<ChildComponent data={this.state.name} />
And to pass functions
<ChildComponent data={this.HandleChange} />
First Send the data from the first child to the common parent using callback
function and then send that received data (stored in state in parent component)
to the second child as props.
you can also read this article - https://www.pluralsight.com/guides/react-communicating-between-components

Add loader on button click in react/redux application

I'm trying to add a Loader as Higher-Order-Component on button click in react/redux application.
Already have working Loader component and styling, just need to set logic when button is clicked show loader and hide existing button.
Button component:
import React from 'react'
import '../../../styles/components/_statement-print.scss';
import Loader from './Loader';
const StatementPrint = (props) => {
return (
<div>
<button
className="print-statement-button"
onClick={props.handleStatementPrint}>PRINT
</button>
</div>
);
};
export default Loader(StatementPrint);
Loader:
import React, { Component} from 'react';
import '../../../styles/components/_loader.scss';
const Loader = (WrappedComponent) => {
return class Loader extends Component {
render() {
return this.props.handleStatementPrint // Where must be logic when to show loader or existing button component
? <button className="loader-button">
<div className="loader">
<span className="loader-text">LOADING...</span>
</div>
</button>
: <WrappedComponent {...this.props} />
}
}
}
export default Loader;
In Loader component i added comment where need to write logic when to set loader or button.
I followed this example: ReactCasts - Higher Order Components
I searched a lot of examples but most of them shows how to set loader then is data is fetching, but in my case i just need to show then onClick method is triggered.
So how to set logic when onClick method is fired? Is this is a good aproach? Also it will be better to try acomplish this doing with redux state, but don't know how to do this.
Any help will be appreciated.
You will have to make small modifications to achieve what you want.
The wrapper component Loader can have a isLoading state, on the basis of which you can decide whether to show the loader span or the wrapped component.
This state isLoading can be updated by the wrapped component by passing showLoader function as a prop.
Button component
import React from 'react'
import '../../../styles/components/_statement-print.scss';
import Loader from './Loader';
const StatementPrint = ({handleStatementPrint, showLoader}) => {
return (
<div>
<button
className="print-statement-button"
onClick={() => {
showLoader();
handleStatementPrint();
}}>
PRINT
</button>
</div>
);
};
export default Loader(StatementPrint);
Loader
import React, { Component} from 'react';
import '../../../styles/components/_loader.scss';
const Loader = (WrappedComponent) => {
return class Loader extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: false
}
this.showLoader = this.showLoader.bind(this);
}
showLoader() {
this.setState({isLoading: true});
}
render() {
return this.state.isLoading
? <button className="loader-button">
<div className="loader">
<span className="loader-text">LOADING...</span>
</div>
</button>
: <WrappedComponent
{...this.props}
showLoader={this.showLoader}
/>
}
}
}
export default Loader;
EDIT
Since handleStatementPrint was required to be called, I have updated the click handler to include that function.
Also using de-structuring to avoid typing props repeatedly. See here for more info.
Just some external state is needed.
If you can't have external state (eg isLoading) than you could pass a function into a loader hoc which will derive isLoading from current props
Example: https://codesandbox.io/s/8n08qoo3j2

React Hight Order Component - Can not use child components in enhanced component

I am trying to implement a Higher Order Component in my react app. I have a base form component with all general login, & then I made a ContactForm component which wraps this general component.
The issue is my page become unresponsive & give maximum stack exceeded error when I try to run it. After some research I found that the issue is calling some custom components in render method of general form component. But that is the same syntax I use everywhere in app.
Why react cause this issue & how to resolve it, Am I implementing the HOC logic in wrong way? I need to import those components in the Form since they handle some logic by themselves & help to separate concerns.
Below is code for both general & HOC component.
Contact Form Component
import React, { Component } from 'react'
import Form from './form'
const createForm = FormComponent =>
class extends Component {
render() {
return <FormComponent {...this.props} />
}
}
const ContactForm = createForm(Form)
export default ContactForm
Base Form Component
import React, { Component } from 'react'
import InputText from './input-text'
import SubmitButton from './submit'
class Form extends Component {
render() {
return (
<div className="page-form">
<div className="page-form-fields clearfix">
<InputText/>
</div>
<SubmitButton />
</div>
)
}
}
export default Form
Input Text
class InputText extends Component {
render() {
const { type, icon, label, name, placeholder, maxlength, value, disabled, error, errorText } = this.props
return (
<div className={`finput ${label && 'labeled'} ${error ? 'has-error' : ''}`}>
<input
type={type || 'text'}
name={name}
className={`textfield w-input ${error ? 'has-error' : ''}`}
maxLength={maxlength}
placeholder={placeholder}
value={value}
disabled={disabled}
onChange={e => this.props.onChange(e)}
onBlur={e => this.props.onBlur && this.props.onBlur(e)}
/>
<label className="fip-label">
{label}
</label>
{error &&
<span className={`fip-info ${error && 'error'}`}>
{errorText}
</span>}
{icon && <i className={`icon icon-${icon}`} />}
</div>
)
}
}
Submit Button
import React, { Component } from 'react'
class SubmitButton extends Component {
render() {
const { response, pending } = this.props
return (
<div className="page-form-submit tright half-top-margin">
{response &&
<h4>
{response}
</h4>}
<button type="button" className="btn" onClick={e => this.props.onSubmit()} disabled={pending}>
Submit
</button>
</div>
)
}
}
export default SubmitButton
It runs fine here,
const {Component} = React
class Form extends Component {
render() {
return (
<div className="page-form">
<div className="page-form-fields clearfix">
<input type="text" />
</div>
<button>Submit</button>
</div>
)
}
}
const createForm = FormComponent =>
class extends Component {
render() {
return <FormComponent {...this.props} />
}
}
const ContactForm = createForm(Form)
ReactDOM.render(<ContactForm />, document.getElementById("app"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

Categories

Resources