Am a bit new to react and redux. I am trying to call/show another stateless component's div from parent component. For example, if I have the below code
const funcA = ({key}) = > {
return (
<div>
<input type="checkbox">
</div>
);
};
export default funcA
const funcB = ({key}) = > {
return (
<React.Fragment>
<div>
<input type="text" placeholder="age" />
</div>
<div id="showThisDiv">
<input type="text" placeholder="age" />
</div>
</React.Fragment>
);
};
export default funcB
I am not trying go with classes. In Stateless components I know how to show/hide a section by using the useState, but I can only do this while in the same component. The issue or rather the challenge, is when I try to show/hide a section from another component. How would I show/hide the div with id showThisDiv in funcB when the checkbox is toggled in funcA?
It's possible using redux, but you also can do it using state and react hooks:
check out this codesandbox: https://codesandbox.io/s/vibrant-driscoll-odqmo
App.js
import React from "react";
import "./styles.css";
import FuncA from "./FuncA";
import FuncB from "./FuncB";
export default function App() {
const [age, setAge] = React.useState(0);
handleInput = (event) => {
console.log(event.target.value);
setAge(age === 0 ? 1 : 0);
}
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<FuncA age={age} onChange={handleInput} />
<FuncB age={age} />
</div>
);
}
FuncA.js
import React from "react";
export default function FuncA(props) {
return (
<>
<label htmlFor="checkbox">
<input type="checkbox" name="age" onChange={props.onChange} value={props.age} />
</label>
</>
);
}
FuncB.js
import React from "react";
export default function FuncB(props) {
return (
<>
{props.age === 0 && <div className="divA">show div a</div>}
{props.age === 1 && <div className="divB">show div b</div>}
</>
);
}
Yeah, is something like this:
EDIT:
// ComponentA.js
const ComponentA = props => <input type="checkbox" onChange={props.onChange} />
// ComponentB.js
const ComponentB = props => (
<>
<div>
<input type="text" placeholder="age" />
</div>
{props.visible && (
<div id="showThisDiv">
<input type="text" placeholder="age" />
</div>
)}
</>
)
// App.js
const App = () => {
const [visible, setVisible] = useState(false);
return (
<>
<ComponentA onChange={() => setVisible(!visible)} />
<ComponentB visible={visible} />
</>
)
}
Now as you want..
Related
I made a form project with react. My form is working well for now but I want add bootstrap modal to my form.
When I click the enter button, I want to show a modal. I found modal example on the web but I can't establish connection between form and modal. This is my form
import React from "react";
import { useState } from "react";
const Contact = () => {
const [showModal, setShowModal] = useState(false);
const [inputValue, setInputValue] = useState("");
const [emailValue, setEmailValue] = useState("");
const [phoneNumberValue, setPhoneValue] = useState("");
const [countryValue, setCountryValue] = useState("");
const buttonOnClick = () => {
if (inputValue === "" || emailValue === "" || phoneNumberValue === "") {
setShowModal(false)
} else {
setShowModal(true)
setInputValue("")
}
console.log(`Form submitted, ${showModal}`);
}
return (
<div className="main">
<form >
<div className="baslik">
<div className="container center">
<h1>Contact Form</h1>
</div>
</div>
<div className="field" >
<label className="text"> Name And Surname: </label>
<input type="text" className="form" placeholder="Kerem Kurt" required value={inputValue} onChange={(e) => setInputValue(e.target.value)} />
</div>
<div className="field">
<label className="text"> E-mail: </label>
<input type="email" className="form" placeholder="udenditfy#gmail.com" required value={emailValue} onChange={(e) => setEmailValue(e.target.value)} />
</div>
<div className="field">
<label className="text"> Phone Number: </label>
<input type="tel" className="form" pattern="[0-9]*" placeholder="+905373199437" required value={phoneNumberValue} onChange={(e) => setPhoneValue(e.target.value)} />
</div>
<div className="field">
<label className="text" required > Country: </label>
<select className="form" placeholder="Turkiye" required value={countryValue} onChange={(e) => setCountryValue(e.target.value)}>
<option value="Turkiye">Turkiye</option>
<option value="Azerbaijan">Azerbaijan</option>
<option value="Japan">Japan</option>
<option value="Serbia">Serbia</option>
<option value="France">France</option>
</select>
</div>
<button type="button" className="button" onClick={() => buttonOnClick()}> Enter </button>
</form>
</div>
);
};
export default Contact;
And this is modal codes;
import React, { useState } from 'react';
import Button from 'react-bootstrap/Button';
import Modal from 'react-bootstrap/Modal';
function Example() {
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
return (
<>
<Button variant="primary" onClick={handleShow}>
Launch static backdrop modal
</Button>
<Modal
show={show}
onHide={handleClose}
backdrop="static"
keyboard={false}
>
<Modal.Header closeButton>
<Modal.Title>Modal title</Modal.Title>
</Modal.Header>
<Modal.Body>
I will not close if you click outside me. Don't even try to press
escape key.
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Close
</Button>
<Button variant="primary">Understood</Button>
</Modal.Footer>
</Modal>
</>
);
}
render(<Example />);
If you want to open your modal after the event "click" in your Form component, you have to import the modal in your form.
For example, your Form is in a component called "Form.jsx" and Modal is in "Modal.jsx" :
Inside Form.jsx, you must import your Modal, move the "show" state into your Form.jsx and pass it in Modal props, just like that :
import Modal from '/path/to/Modal.jsx'
const [show, setShow] = useState(false);
<form>
{children}
</form>
<Modal show={show} setShow={setShow}/>
Inside your Modal.jsx, just change a few things :
function Modal(props){
// remove the show State
// Remove the Button with "handleShow" function.
const handleClose = () => props.setShow(false);
<Modal show={props.show}>
<Button onClick={handleClose}>Close</Button>
</Modal>
}
Welcome to world of react development!
Your question is a classic question about sharing state between components. This is what you refer to as "connection" between components.
The solution is generally referred to as "lifting state up". I highly recommend reading through that if you haven't already.
Example:
import React from 'react';
import ReactDOM from 'react-dom/client';
import { useState } from "react";
import Button from 'react-bootstrap/Button';
import Modal from 'react-bootstrap/Modal';
const root = ReactDOM.createRoot(document.getElementById('root'));
const Contact = ({toggleShow}) => {
return (
<div className="main">
<form >
<button type="button" className="button" onClick={() => toggleShow()}> Show Modal </button>
</form>
</div>
);
};
const MyModal = ({show, toggleShow}) => {
return (
<>
<Modal
show={show}
onHide={toggleShow}
backdrop="static"
keyboard={false}
>
<Button variant="secondary" onClick={toggleShow}>
Close
</Button>
</Modal>
</>
);
}
const App = () => {
const [show, setShow] = useState(false);
const toggleShow = () => setShow(!show);
return (
<>
<Contact toggleShow={toggleShow}/>
<MyModal show={show} toggleShow={toggleShow}/>
</>
)
}
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
Explanation:
The general idea is to share the show state of the Modal as well as the state toggler toggleShow() between our <Contact> and <MyModal> components.
We can easily achieve this:
create a new parent component <App> (the name is arbitrary) where we define the show state and the toggler function.
pass the state and toggler into the modal: <MyModal state={state} toggleShow={toggleShow} />.
the form only needs the toggler: <Form toggleShow={toggleShow} />
Note that toggleShow can also be split into separate handleShow and handleClose function, but this approach saves us a few lines.
I have an input field in my react project to display a separate component when it's in a hover state. I have a separate component called [Focus Style] from the Actual component but all I'm doing is not working. The focus state should work with the first input field Kindly assist me. My code below.
Main Componenet
import React, { useState, useEffect } from "react";
import stays from "./Components/stays.json";
// import styled, {css} from "styled-components";
import FocusStyle from "../src/Components/FocusStyle";
export default function SearchGuest() {
const [guest, setGuest] = useState("");
const [location, setLocation] = useState("");
const [Data, setData] = useState([]);
const [filteredData, setFilteredData] = useState(Data);
const [focus, setFocus] = useState("false");
const onFocusChange = () => {
setFocus("true");
console.log("focused");
};
useEffect(() => {
setData(stays);
setFilteredData(stays);
}, []);
const handleSearch = (event) => {
event.preventDefault();
};
return (
<div>
<form action="" >
<input
onFocus={onFocusChange}
type="text"
name="guest"
id=""
// value={guest}
placeholder="text"
onChange={handleSearch}
style={focus ? { border: "1px solid yellow" } : <FocusStyle/> }
/>
<input
type="number"
name="location"
id=""
// value={location}
placeholder="number"
onChange={handleSearch}
/>
<button value="submit" >Search</button>
</form>
{console.log(filteredData)}
</div>
);
}
FocusStyle Component
import React from 'react'
export default function FocusStyle() {
return (
<div>
<h2 style={{marginTop: "90px", color: 'pink'}}>Focus Componenet</h2>
<div className="modal">
<div className="adult">
<span>Adult</span>
<button>-</button><span>0</span><button>+</button>
</div>
<div className="children">
<span>Children</span>
<button>-</button><span>0</span><button>+</button>
</div>
</div>
</div>
)
}
if so then you can do this
{focus ? <input
onFocus={onFocusChange}
type="text"
name="guest"
id=""
// value={guest}
placeholder="text"
onChange={handleSearch}
style={{ border: "1px solid yellow" }}
/>:
<FocusStyle/>
}
I have a problem with the access of the input value, I used here is .map().
Here is the code, <QuestionLabel/>is the children component. projectsData.projectDetail is an available data
//..
{projectsData.projectDetail.questions.map((question) => (
<QuestionLabel
questionTitle={question}
/>
))}
//child component
const QuestionLabel=(props)=>{
const [answerInput, setAnswerInput] = React.useState("");
return(
<div className="contact">
<form className="contact-form" autocomplete="off">
<div class="contact-form-group">
<label
for="name"
class="contact-form-label"
>
{props.questionTitle}
</label>
<input
id="name"
type="text"
class="contact-form-input"
value={answerInput}
onChange={(answer) => setAnswerInput(answer.target.value)}
/>
</div>
</form>
</div>
);
}
export default QuestionLabel;
There are many way to get value from child component for parent component. You can call a function pass from parent to children to set value for parent state when it's changed. Exmaple:
const ParentComponent =(props)=>{
const [valueFromChild, setValueFromChild] = useState('');
return <>
{valueFromChild}
<QuestionLabel questionTitle={'Title'} setValueFromChild={setValueFromChild}/>
</>
}
const QuestionLabel=(props)=>{
const [answerInput, setAnswerInput] = React.useState("")
useEffect(() => {
props.setValueFromChild(answerInput);
}, [answerInput]);
return(
<div className="contact">
<form className="contact-form" autoComplete="off">
<div class="contact-form-group">
<label
for="name"
class="contact-form-label"
>
{props.questionTitle}
</label>
<input
id="name"
type="text"
class="contact-form-input"
value={answerInput}
onChange={(answer) => setAnswerInput(answer.target.value)}
/>
</div>
</form>
</div>
);
}
So you need to have the input state up in the parent.
Since you are mapping through questions array to render the QuestionLabel we can try this...
//..
const ParentComponent = () => {
const [answers, setAnswers] = React.useState({})
useEffect(() => {
projectsData.projectDetail.questions.forEach((_, i) => {
setAnswers(previousAnswers => {...previousAnswers, ["name" + i]: ""})
})
}, [])
const handleAnswers = (e) => {
setAnswers(previousAnswers => {...previousAnswers, [e.target.name]: [e.target.value]})
}
//..
then
//..
{
projectsData.projectDetail.questions.map((question, i) => {
return (
<QuestionLabel questionTitle={question} inputName={"name" + i} answers={answers} handleAnswers={handleAnswers} />
)
})
}
and finally...
//child component
const QuestionLabel=(props)=>{
return(
<div className="contact">
<form className="contact-form" autocomplete="off">
<div class="contact-form-group">
<label
for={props.inputName}
class="contact-form-label"
>
{props.questionTitle}
</label>
<input
id={props.inputName}
name={props.inputName}
type="text"
class="contact-form-input"
value={answers[props.inputName]}
onChange={handleAnswers}
/>
</div>
</form>
</div>
);
}
export default QuestionLabel;
I am new to ReactJS and building one simple application using few components like UserForm, UserGrid and App.
UserForm displays simple textbox and button like below.
and when user input some text inside textbox and hit the save button it will display data in UserGrid component as per the screenshot.
but i can't figure it out how edit will work ? like when i hit the edit it will fetch the information from the grid and fill the textbox so i can update the data, can someone please help me on this ? how can i achieve this ?
below is my code
UserForm.js
import React,{createRef} from 'react';
const UserForm = (props) =>{
const username = createRef();
const saveUser = () =>{
debugger
if(username.current.value !== ''){
props.handleSubmit(username.current.value);
username.current.value='';
username.current.focus();
}
}
return(
<div className="row">
<div className="col-md-6">
<div className="form-group">
<input type="text" className="form-control" placeholder="Username" ref={username} />
</div>
</div>
<div className="col-md-12">
<button className="btn btn-primary" onClick={saveUser}>Save</button>
</div>
</div>
)
}
export default UserForm;
UserGrid.js
import React from 'react';
const UserGrid = (props) =>{
debugger
return(
<div className="row">
<div className="col-md-12">
<table className="table">
<tr>
<th>
Username
</th>
<th>
</th>
</tr>
{
props.list.map(item =>
<tr>
<td>
{item}
</td>
<td>
<button>Edit</button>
<button>Delete</button>
</td>
</tr>
)
}
</table>
</div>
</div>
)
}
export default UserGrid;
App.js
import React,{useState} from 'react';
import UserForm from './UserForm';
import UserGrid from './UserGrid';
function App() {
const [list, setList] = useState([]);
const handleSubmit = (username) =>{
setList([...list,username]);
}
return (
<div className="App">
<UserForm handleSubmit={handleSubmit}></UserForm>
<UserGrid list={list}></UserGrid>
</div>
);
}
export default App;
You have to bring your edit state up to App component rather than using ref in the child component. Just like you have handleSubmit in App.js have a handleEdit function and pass that function to UserGrid.
function App() {
const [list, setList] = useState([]);
const [name, setName] = useState('');
const [editIndex, setEditIndex] = useState(null);
const handleSubmit = () => {
if(editIndex) {
setList([...list.slice(0, index), name, ...list.slice(index+1)])
setEditIndex(null)
} else {
setList([...list, name]);
}
setName('')
}
return (
<div className="App">
<UserForm name={name} setName={setName} handleSubmit={handleSubmit}></UserForm>
<UserGrid setEditIndex={setEditIndex} list={list}></UserGrid>
</div>
);
}
UserForm.js
import React,{createRef} from 'react';
const UserForm = ({name, setName}) =>{
const saveUser = () =>{
debugger
if(name !== ''){
props.handleSubmit();
}
}
return(
<div className="row">
<div className="col-md-6">
<div className="form-group">
<input type="text" className="form-control" placeholder="Username" value={name} onChange={(e) => setName(e.target.value)} />
</div>
</div>
<div className="col-md-12">
<button className="btn btn-primary" onClick={saveUser}>Save</button>
</div>
</div>
)
}
export default UserForm;
To support edit:
{
props.list.map((item, index) =>
<tr>
<td>
{item}
</td>
<td>
<button onClick={setEditIndex(index)}>Edit</button>
<button>Delete</button>
</td>
</tr>
)
}
This is working example while i am using your code.
Nothing special in App.js. You can take a look a logic.
App.js
import React, { useState } from "react";
import UserForm from "./UserForm";
import UserGrid from "./UserGrid";
function App() {
const [list, setList] = useState([]);
const handleSubmit = username => {
setList([...list, username]);
};
const editList = item => {
setList(
list.map(l => ({
...l,
username: l.id === item.id && item.newValue ? item.newValue : l.username
}))
);
};
const deleteList = id => {
setList(list.filter(l => l.id !== id));
};
return (
<div className="App">
<UserForm handleSubmit={handleSubmit}></UserForm>
<UserGrid
list={list}
editList={editList}
deleteList={deleteList}
></UserGrid>
</div>
);
}
export default App;
This is UserForm Component. I think you will have problems, if you dont have someting like ID, cuz if you have lists with same names, your delete and edit function will work wrong.
UseForm.js
import React, { createRef } from "react";
const UserForm = ({ handleSubmit }) => {
const username = createRef();
const saveUser = () => {
if (username.current.value !== "") {
const randomId = () => "_" + Math.random().toString(36).substr(2, 9);
handleSubmit({ username: username.current.value, id: randomId() });
username.current.value = "";
username.current.focus();
}
};
return (
<div className="row">
<div className="col-md-6">
<div className="form-group">
<input
type="text"
className="form-control"
placeholder="Username"
ref={username}
/>
</div>
</div>
<div className="col-md-12">
<button className="btn btn-primary" onClick={saveUser}>
Save
</button>
</div>
</div>
);
};
export default UserForm;
And this is UserGrid component. I hope you will undarstand the code. If you have questions, go ahead.
UseGrid.js
import React, { useState } from "react";
const UserGrid = ({ list, editList, deleteList }) => {
const [isEditable, setIsEditable] = useState(false);
const [value, setValue] = useState("");
return (
<div className="row">
<div className="col-md-12">
<table className="table">
<tr>
<th>Username</th>
<th></th>
</tr>
{list.map(item => (
<tr>
<td>{item.username}</td>
{isEditable ? (
<>
<td>
<input
type="text"
className="form-control"
placeholder="Username"
onChange={e => setValue(e.target.value)}
/>
</td>
<button onClick={() => setIsEditable(false)}>CANCEL</button>
</>
) : null}
<td>
<button
onClick={() => {
editList({
id: item.id,
username: item.username,
newValue: value
});
setIsEditable(true);
}}
>
Edit
</button>
<button onClick={() => deleteList(item.id)}>Delete</button>
</td>
</tr>
))}
</table>
</div>
</div>
);
};
export default UserGrid;
How to set defaultValue to input component?
<Field name={`${prize}.rank`} defaultValue={index} component={Input} type='text'/>
I tried like above but my fields are empty. I'm trying to create fieldArray (dynamic forms):
{fields.map((prize, index) =>
<div key={index} className="fieldArray-container relative border-bottom" style={{paddingTop: 35}}>
<small className="fieldArray-title marginBottom20">Prize {index + 1}
<button
type="button"
title="Remove prize"
className="btn btn-link absolute-link right"
onClick={() => fields.remove(index)}>Delete</button>
</small>
<div className="row">
<div className="col-xs-12 col-sm-6">
<Field name={`${prize}.rank`} defaultValue={index} component={Input} type='text'/>
<Field name={`${prize}.prizeId`} defaultValue={index} component={Input} type='text'/>
<Field
name={`${prize}.name`}
type="text"
component={Input}
label='Prize Name'/>
</div>
<div className="col-xs-12 col-sm-6">
<Field
name={`${prize}.url`}
type="text"
component={Input}
label="Prize URL"/>
</div>
<div className="col-xs-12">
<Field
name={`${prize}.description`}
type="text"
component={Input}
label="Prize Description" />
</div>
</div>
</div>
)}
On redux forms you can call initialize() with an object of values like so:
class MyForm extends Component {
componentWillMount () {
this.props.initialize({ name: 'your name' });
}
//if your data can be updated
componentWillReceiveProps (nextProps) {
if (/* nextProps changed in a way to reset default values */) {
this.props.destroy();
this.props.initialize({…});
}
}
render () {
return (
<form>
<Field name="name" component="…" />
</form>
);
}
}
export default reduxForm({})(MyForm);
This way you can update the default values over and over again, but if you just need to do it at the first time you can:
export default reduxForm({values: {…}})(MyForm);
This jsfiddle has an example
https://jsfiddle.net/bmv437/75rh036o/
const renderMembers = ({ fields }) => (
<div>
<h2>
Members
</h2>
<button onClick={() => fields.push({})}>
add
</button>
<br />
{fields.map((field, idx) => (
<div className="member" key={idx}>
First Name
<Field name={`${field}.firstName`} component="input" type="text" />
<br />
Last Name
<Field name={`${field}.lastName`} component="input" type="text" />
<br />
<button onClick={() => fields.remove(idx)}>
remove
</button>
<br />
</div>
))}
</div>
);
const Form = () => (
<FieldArray name="members" component={renderMembers} />
);
const MyForm = reduxForm({
form: "foo",
initialValues: {
members: [{
firstName: "myFirstName"
}]
}
})(Form);
this is my implementation using a HoC
import { Component } from 'react'
import {
change,
} from 'redux-form'
class ReduxFormInputContainer extends Component{
componentDidMount(){
const {
initialValue,
meta,
} = this.props
if(initialValue === undefined || meta.initial !== undefined || meta.dirty) return
const {
meta: { form, dispatch },
input: { name },
} = this.props
dispatch(change(form, name, initialValue))
}
render(){
const {
initialValue,
component: Compo,
...fieldProps
} = this.props
return <Compo {...fieldProps} />
}
}
function reduxFormInputContainer(component){
return function(props){
return <ReduxFormInputContainer {...props} component={component} />
}
}
export default reduxFormInputContainer
and then for exemple:
import reduxFormInputContainer from 'app/lib/reduxFormInputContainer'
InputNumericWidget = reduxFormInputContainer(InputNumericWidget)
class InputNumeric extends Component{
render(){
const props = this.props
return (
<Field component={InputNumericWidget} {...props} />
)
}
}