How to communicate between sibling components in React - javascript

How to transfer props from one sibling to another, please?
In main app I have only 2 siblings:
Input:
import React from "react";
const Input = () => {
return (
<>
<label htmlFor="name">Full Name</label>
<input type="text" id="name" placeholder="TheDareback" />
<label htmlFor="job">Job Title</label>
<input type="text" id="job" placeholder="Frontend Developer" />
</>
);
};
export default inputDetails;
Preview:
import React from "react";
const Preview = () => {
return (
<>
<table>
<thead>
<tr>
<th>{/* here I need a Full Name from the input */}</th>
<th>{/* here I need a Job Title from the input*/}</th>
</tr>
</thead>
</table>
</>
);
};
export default Preview;
I tried to add useEffect to Preview to read the value of each input.
for example
const name = document.querySelector("#name");
const [inputName, setInputName] = useState("TheDareback");
useEffect(() => (name.value ? setInputName(name.value) : null));
But I'm still getting an error:
Preview.js:9 Uncaught TypeError: Cannot read properties of null (reading 'value')
Is it possible to run useEffect only during the second rendering? Alternatively, is there another option than to move the props directly from Input.js where would I create a handler for each onChange in input?
Thank you very much for heading in the right direction.

Keep the state in the App component and give setter to Input and state to Preview component.
App.js
import Input from "./Input";
import Preview from "./Preview";
export default function App() {
const [state, setState] = useState({ name: "", job: "" });
return (
<div className="App">
<Input setState={setState} />
<Preview state={state} />
</div>
);
}
Input.js
import React from "react";
const Input = ({ setState }) => {
const onChangeHandler = (e) => {
setState((prevState) => ({
...prevState,
[e.target.id]: e.target.value
}));
};
return (
<>
<label htmlFor="name">Full Name</label>
<input
type="text"
id="name"
placeholder="TheDareback"
onChange={onChangeHandler}
/>
<label htmlFor="job">Job Title</label>
<input
type="text"
id="job"
placeholder="Frontend Developer"
onChange={onChangeHandler}
/>
</>
);
};
export default Input;
Preview.js
import React from "react";
const Preview = ({ state: { name, job } }) => {
return (
<>
<table>
<thead>
<tr>
<th>{name}</th>
<th>{job}</th>
</tr>
</thead>
</table>
</>
);
};
export default Preview;
Code Sandbox

You need some sort of a container component (or App.js for example), you can have an input state there, and an inputHandler function to pass to the Input component and put it with an onChange for example.
import React, {useState} from 'react';
export default function Container() {
const [input, setInput] = useState({
name: '',
job: ''
})
const inputHandler = (e, type) => {
setInput(prevState => ({...prevState, [type]: e.target.value}))
}
return (
<>
<Input onInputChange={inputHandler} />
<Preview input={input} />
</>
);
}
Input:
const Input = ({onInputChange}) => {
return (
<>
<label htmlFor="name">Full Name</label>
<input type="text" id="name" placeholder="TheDareback" onChange={(e) => onInputChange(e, 'name')} />
<label htmlFor="job">Job Title</label>
<input type="text" id="job" placeholder="Frontend Developer" onChange={(e) => onInputChange(e, 'job')} />
</>
);
};
export default Input;
Preview:
const Preview = ({input}) => {
return (
<>
<table>
<thead>
<tr>
<th>{input.name}</th>
<th>{input.job}</th>
</tr>
</thead>
</table>
</>
);
};
export default Preview;

Related

Failed to get state by useLocation while it has Object data in state

First.js
import { useState } from "react";
import { Link } from "react-router-dom";
const First = () => {
const [name, setName] = useState("");
const [phone, setPhone] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
}
return (
<div className="First">
<h1>This is First Input Page</h1>
<form onSubmit={handleSubmit}>
<dd>data 1</dd>
<input
type="text"
value={name}
onChange={(e) =>
setName(e.target.value)
}
required
></input>
<dd>data 2</dd>
<input
type="text"
value={phone}
onChange={(e) =>
setPhone(e.target.value)
}
required
></input><br/>
<Link to={'/second'} state={{ state: { name : name , phone : phone } }}><button>submit</button></Link>
</form>
</div>
);
}
export default First;
I try to send Object data using Link/state to another component.
Second.js
import {useLocation} from 'react-router-dom';
const Second = () => {
const location = useLocation();
console.log(location.state);
console.log(location.state.name);
return (
<div className="Second">
<h1>This is Second Input Page</h1>
<form>
<dd>data 3</dd>
<input></input>
<dd>data 4</dd>
<input></input><br/>
<button>submit</button>
</form>
</div>
);
}
export default Second;
However, while I can access to (location.state), I can not access to (location.state.name). Why is that?
Output
state: {name: 'Myname', phone: 'myphone'}
[[Prototype]]: Object
--------------------
undefined
The output shows that the code line "console.log(location.state);" works, but to the "console.log(location.state.name);", it shows undefined.
It's because you passed an object with state as the root property, i.e.
state={{ state: { name: name, phone: phone } }}
so to access it in the receiving route it is location.state.state.name.
You really don't need to nest the data you want to pass under a state property when using the Link component, it's not quite the same as when using the navigate function.
<Link to="/second" state={{ name, phone }}>
<button>submit</button>
</Link>
It may also be considered semantically incorrect HTML to nest a button element within an anchor tag (via Link) element. Use the useNavigate hook and issue an imperative navigation action from the form's submit handler. In this case the navigate function's second argument, the options object, *does* expect the state to be passed under the state` property.
Example:
import { Link, useNavigate } from "react-router-dom";
const First = () => {
const navigate = useNavigate();
const [name, setName] = useState("");
const [phone, setPhone] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
navigate("/second", { state: { name, phone } });
};
return (
<div className="First">
<h1>This is First Input Page</h1>
<form onSubmit={handleSubmit}>
<dd>data 1</dd>
<input
type="text"
value={name}
onChange={(e) =>
setName(e.target.value)
}
required
/>
<dd>data 2</dd>
<input
type="text"
value={phone}
onChange={(e) =>
setPhone(e.target.value)
}
required
/>
<br/>
<button>submit</button>
</form>
</div>
);
};
export default First;

How do I get the Age data from child to parent component

Parent.js
import React, { useState } from 'react'
import Child from './Child'
const Parent = () => {
const[data,setData] = useState('')
const[name,setName] = useState('')
const[toggled,setToggled] = useState(false)
const[age,setAge] = useState('')
const ageToChild = (ageData) =>{
setAge(ageData)
}
const childToParent = (childData) =>{
setData(childData)
}
const handleSubmit = (e) =>{
e.preventDefault()
alert(`Age is ${age} , Form Submitted`)
console.log(`Name is ${name} and Age is ${age}`)
}
return (
<>
{data}
<div>
{/* <Child childToParent={childToParent}></Child> */}
<form onSubmit={handleSubmit}>
<label>Name : </label>
<input type="text" value={name} onChange={(e)=>setName(e.target.value)}></input>
<button type='button' onClick={()=>setToggled(!toggled)}>Age ?</button>
{toggled && <Child childToParent={childToParent} ageToChild={ageToChild}></Child>}
<button type='button'>Submit</button>
</form>
</div>
</>
)
}
export default Parent
Child.js
import React from 'react'
const Child = ({childToParent,ageToChild}) => {
const data = "This is some data from the child component to parent"
// const age = ageToChild
return (
<>
<button onClick={()=>childToParent(data)}>Click Child</button>
<label>Age : </label>
<input type='text' onChange={()=>ageToChild()}></input>
</>
)
}
export default Child
I am getting output as Name is inputtedname and Age is Undefined , How do I get the user inputted age value logged in the console?
I am trying to pass data from child to parent using functional components
If you want to pass age value to alert() from Child.js component, you should add e.target.value to onChange:
<input type='text' onChange={(e)=>ageToChild(e.target.value)}></input>
Make sure to pass the ageToChild function from the parent component to the child:
<Child childToParent={childToParent} ageToChild={ageToChild} />
Then from the Child component you can simply do:
<input type='text' onChange={(e)=> ageToChild(e.target.value)} />
use this code:
<input type='text' onChange={(e)=>ageToChild(e.target.value)}></input>
You have forgotten to pass the value in the function😁. Try passing it in onChange.
onChange={(e)=>ageToChild(e.target.value)}

How to create dynamic (conditional) placeholder in React.js

So I have a submit form where the user needs to create a task by typing in a task name. I want it to be empty at the beginning and have a placeholder of "you must enter a task" when the user click add without entering anything. Now I can achieve it to display the placeholder but it's either always there or I encounter unreachable code. I know how to clean the submission & return to the add function, just need to be able to display the placeholder conditionally. Here's what my code looks like atm:
import { useState } from "react";
export default function Todos() {
const [todos, setTodos] = useState([{ text: "hey" }]);
const [todoText, setTodoText] = useState("");
const [isEmpty, setEmpty] = useState("false");
const addTodo = (e) => {
e.preventDefault();
if (todoText){
setTodos([...todos, { text: todoText }]);
setTodoText("");
} else {
setEmpty(true)
setTodoText("");
return
}
}
return (
<div>
{todos.map((todo, index) => (
<div key={index}>
<input type="checkbox" />
<label>{todo.text}</label>
</div>
))}
<br />
<form onSubmit={addTodo}>
<input
value={todoText}
onChange={(e) => setTodoText(e.target.value)}
type="text"
></input>
<button type="submit">Add</button>
{isEmpty &&<span style={{ color: "red" }}>Enter a task</span>}
</form>
</div>
);
}
I could change your code with the following:
You need to initialize isEmpty by false instead of string "false".
And you can use this flag on showing placeholder texts.
Note that I renamed isEmpty by showError.
import { useState } from "react";
export default function Todos() {
const [todos, setTodos] = useState([{text: "hey"}]);
const [todoText, setTodoText] = useState("");
const [showError, setShowError] = useState(false);
// #ts-ignore
const addTodo = (e) => {
e.preventDefault();
if (todoText) {
setTodos([...todos, {text: todoText}]);
setTodoText("");
setShowError(false);
} else {
setTodoText("");
setShowError(true);
return
}
}
return (
<div>
{todos.map((todo, index) => (
<div key={index}>
<input type="checkbox"/>
<label>{todo.text}</label>
</div>
))}
<br/>
<form onSubmit={addTodo}>
<input
value={todoText}
onChange={(e) => setTodoText(e.target.value)}
type="text"
></input>
<button type="submit">Add</button>
{(showError && !todoText) && <span style={{color: "red"}}>Enter a task</span>}
</form>
</div>
);
}

TypeError: songs.map is not a function

Hi I'm new to react Every time when i run the code it shows the same error. Can anyone explain what is wrong with this code.
import React, { Component } from 'react'
import '../Search.css'
import axios from 'axios'
export default class Search extends Component {
state = {
songs: [],
};
componentDidMount(key) {
axios.get(`https://cors-anywhere.herokuapp.com/https://itunes.apple.com/search?term=`+key).then(res => {
console.log(res);
this.setState({songs: res.data});
});
}
render() {
const {songs} = this.state;
return (
<div className="container-fluid">
<input
type="text"
name="search"
placeholder="Search..."
id="search"
onChange={(event) => this.componentDidMount(event.target.value)}
/>
<div>
{songs.map(song => (
<h1>{song.artistName}</h1>
))}
</div>
</div>
)
}
}
The map method is available only for an Array type. This means that res.data is not an array. Please inspect the response coming from the request URL first.
You should display the data only when it's available:
render() {
const {songs} = this.state;
return (
<div className="container-fluid">
<input
type="text"
name="search"
placeholder="Search..."
id="search"
onChange={(event) => this.componentDidMount(event.target.value)}
/>
<div>
{songs && songs.map(song => (
<h1>{song.artistName}</h1>
))}
</div>
</div>
)
}
}

How do I reference a input element from higher order component

I would like to access the input element from my HOC. How can I achieve this?
const TextInput = (props) => {
const allowed = ['readOnly','tabIndex','placeholder'];
const filteredProps = filterProps(props,allowed);
return (
<div>
<label>{props.field.Name}</label>
<input type="text" ref={props.inputRef} key={props.field.Id} className="form-control" id={props.field.Id} name={props.field.Name}
value={props.value}
onChange={props.onChange}
onKeyDown={props.onKeyDown}
{...filteredProps}
/>
</div>
);
}
TextInput.propTypes = {
fieldMetadata: PropTypes.object,
isValid: PropTypes.bool
}
export default withInputMask(withRequired(withReadOnly(withMinMax(withHidden(TextInput)))));
I have tried a few things but this is the latest attempt.
Inside the withInputMask render method I have inserted the following.
return (
<div>
<Component {...this.props} inputRef={el=> this.inputElement=el} isValid={isValid} onChange={this.onChange} placeholder={inputMaskPattern} />
{hasErrors && <span className={hasErrors}>{error}</span>}
</div>
);
}
}
};
export default withInputMask;
when I open the react dev tools and click on withInputMask component this is what I see.

Categories