React, Calling a function from another component - reactjs

As stated in the question I want to call a function declared in another component. Here's some example data,
function BookingTable() {
const renderTableData = (startId) => {
let id = startId;
}
}
export default BookingTable;
How do i access the renderTableData from another component?

If the function should be accessible is the child component of the component which has the function. Then you can pass the function through props.
But the best option for this is context api. With that you can access the function in multiple components.
Context api helps you share the states and functions of a component
with other components inside the particular project.
In Filecontext.jsx you can see createContext which helps you in creating a context.
In App.jsx, we have created the states and functions which has to be shared among the components and wrapped the components which can access the datas with that context by importing it.
In Formcomponent.jsx, I am using useContext to use the states and functions created in the App.jsx.
Filecontext.jsx
import { createContext } from 'react'
export const Filecontext = createContext({});
App.jsx
import { Filecontext } from './Contexts/Filecontext';
import { useState } from 'react'
function App() {
const [name, setName] = useState("")
const [email, setEmail] = useState("")
const [mobileno, setMobileno] = useState("")
const showAlert = () => {
alert(`Hello ${name}`);
}
return (
<div className="App">
<Filecontext.Provider value={{ name, setName, email, setEmail, mobileno, setMobileno, showAlert }}>
<Formcomponent />
<Listcomponent />
</Filecontext.Provider>
</div>
);
}
export default App;
Formcomponent.jsx
import { Filecontext } from '../Contexts/Filecontext';
import { useContext } from 'react'
export default function Formcomponent() {
const { setName, setEmail, setMobileno, showAlert } = useContext(Filecontext)
return (
<>
<div className="form-group">
<label>Name : </label>
<input type="text" onChange={(e) => { setName(e.target.value) }} />
</div>
<div className="form-group">
<label>Email : </label>
<input type="email" onChange={(e) => { setEmail(e.target.value) }} />
</div>
<div className="form-group">
<label>Mobile No : </label>
<input type="number" onChange={(e) => { setMobileno(e.target.value) }} />
</div>
<div className="form-group">
<input type="submit" value="submit" onClick={() => { showAlert() }} />
</div>
</>
)
}

function BookingTable() {
const renderTableData = (startId) => {
let id = startId;
}
return (
<BookingTable2 renderTableData={renderTableData} />
)
}
export default BookingTable;
const BookingTable2 = ({renderTableData}) => {
const onClickHandler = () => {
renderTableData()
}
return (
<button onClick={onClickHandler}>Calling func from child component</button>
)
}
export default BookingTable;

Bear in mind that React FC are just JS functions that return JSX (in most cases) so you can't access variables that were declared inside of them from outside of the component. The solution to that problem would be to pass the function as props to the child components.

Related

passing a variable from child component to parent component in Next.js

I have 2 components home and tiny tiny is imported inside home as u can see in the code
I am trying to pass value.toString("html") from tiny.js to home.js
if this is not possible at least help me integrate both tiny and home components as a single object so that I don't have to pass the value as props to a parent component
import React from "react";
import Tiny from "./tiny";
function Home({ data }) {
const [Questions, setQuestions] = useState();
const [deatils1, setdeatils] = useState();
function clickQuestion() {
axios
.post("https://askover.wixten.com/questionpost", {
Name: Questions,
Summary: deatils1,//pass tiny value as summery
})
.then(() => {
window.location.reload();
});
}
function question(e) {
setQuestions(e.target.value);
}
return (
<>
<div>
<div className="container search-box">
<Form>
<Form.Group className="mb-3" controlId="exampleForm.ControlInput1">
<Form.Label>Title</Form.Label>
<Form.Control
type="text"
onChange={question}
placeholder="ask anything?"
/>
</Form.Group>
<Tiny /> //tiny component
</Form>
<Button
type="submit"
disabled={!deatils1 || !Questions}
onClick={clickQuestion}
variant="outline-secondary"
id="button-addon2"
>
ask?
</Button>
</div>
</div>
</>
);
}
tiny.js
import React, { useState, useEffect } from "react";
import dynamic from "next/dynamic";
import PropTypes from "prop-types";
//import the component
const RichTextEditor = dynamic(() => import("react-rte"), { ssr: false });
const MyStatefulEditor = ({ onChange }) => {
const [value, setValue] = useState([]);
console.log(value.toString("html"));
useEffect(() => {
const importModule = async () => {
//import module on the client-side to get `createEmptyValue` instead of a component
const module = await import("react-rte");
console.log(module);
setValue(module.createEmptyValue());
};
importModule();
}, []);
const handleOnChange = (value) => {
setValue(value);
if (onChange) {
onChange(value.toString("html"));
}
};
return <RichTextEditor value={value} onChange={handleOnChange} />;
};
MyStatefulEditor.propTypes = {
onChange: PropTypes.func,
};
export default MyStatefulEditor;
Actually, you already have onChange event in tiny, so you only need to pass another onChange event from home to tiny.
import React from "react";
import Tiny from "./tiny";
function Home({ data }) {
const [Questions, setQuestions] = useState();
const [details, setDetails] = useState();
function clickQuestion() {
axios
.post("https://askover.wixten.com/questionpost", {
Name: Questions,
Summary: details,//pass tiny value as summery
})
.then(() => {
window.location.reload();
});
}
function question(e) {
setQuestions(e.target.value);
}
return (
<>
<div>
<div className="container search-box">
<Form>
<Form.Group className="mb-3" controlId="exampleForm.ControlInput1">
<Form.Label>Title</Form.Label>
<Form.Control
type="text"
onChange={question}
placeholder="ask anything?"
/>
</Form.Group>
<Tiny onChange={(value) => setDetails(value)}/> //tiny component
</Form>
<Button
type="submit"
disabled={!deatils1 || !Questions}
onClick={clickQuestion}
variant="outline-secondary"
id="button-addon2"
>
ask?
</Button>
</div>
</div>
</>
);
}

How do I transfer the value of the inputs from one page to another?

On one page I have two inputs and a button, after clicking on the button goes to the second page, how do I get data from the inputs on the second page?
navigate(path, { state: { input_value: value }}) ?
index.js
import './App.css';
function App() {
return (
<div className='App'>
<header className='App-header'>
<input type='text' placeholder='Name'/>
<input type='text' placeholder='Surname'/>
<button type='submit'>Send</button>
</header>
</div>
);
}
export default App;
getData.js
export const getData = () => {
return (
<div>
<h1>Name:</h1>
<h1>Surname:</h1>
</div>
)
};
You can have a state variable in the App component and then pass the state as a prop to GetData:
import './App.css';
import { useState, useRef } from "react";
function App() {
const nameInput = useRef(null);
const surNameInput = useRef(null);
const [fullName, setFullName] = useState({
name: "",
surName: ""
});
const sendData = () => {
// you can do some input validation here
setFullName({
name: nameInput.current.value,
surName: surNameInput.current.value,
});
}
return (
<div className='App'>
<header className='App-header'>
<input type='text' placeholder='Name'
ref={nameInput}
/>
<input type='text' placeholder='Surname'
ref={surNameInput}
/>
<button onClick={sendData}>Send</button>
</header>
<!-- some more markup here -->
</div>
);
}
export default App;
Here's how you pass your data to GetData component:
<GetData fullName={fullName} />
And then in your GetData component you get the passed props and display them:
export const GetData = (fullName) => {
return (
<div>
<h1>Name: {fullName.name}</h1>
<h1>Surname: {fullName.surName}</h1>
</div>
)
};
You can read more about hooks like useState and useRef here
So this might be Part 1 of an answer.
Taking the code you have, I've put the fields in form tag, then called handleSubmit from the built in onSubmit that is called when you click a button of type='submit' inside a form.
The values is taken from the event parameter (e) of the onSubmit (you can identify these by the 'name' attribute on the input tags) and then I am using useState hook to store the two values.
This would be where part one ends. You have the data, and you can see how it is passed to the GetDate component (the deconstructed props {name, surname} that are passed in.
From there, you should follow the documentation for your chosen router and, if you run into trouble, post the code you've tried and I can continue to help.
https://v5.reactrouter.com/web/guides/quick-start
import React, { useState } from 'react';
import { GetData } from './GetData';
export function App() {
const [theName, setTheName] = useState('');
const [theSurname, setTheSurname] = useState('');
const handleSubmit = (e) => {
setTheName(e.target.name.value);
setTheSurname(e.target.surname.value);
e.preventDefault();
}
return (
<div className='App'>
<header className='App-header'>
<form onSubmit={handleSubmit}>
<input type='text' placeholder='Name' name='name'/>
<input type='text' placeholder='Surname' name='surname'/>
<button type='submit'>Send</button>
</form>
</header>
<GetData name={theName} surname={theSurname} />
</div>
);
}
export default App;
Here is a component like your getData function.
I've added it to the App component, just so you can see the values being displayed, but for what you are looking for, you will need to read the documentation for react-router-dom
import React from "react";
export const GetData = ({name, surname}) => {
return (
<div>
<h1>Name:{name}</h1>
<h1>Surname:{surname}</h1>
</div>
)
};

Map trough context in react

I'm trying to map trough my context in React but it won't render anything. (No error messages either). My app is wrapped with the context provider.
My context
import { createContext, useState } from "react";
export const ExpenseContext = createContext();
export const ExpenseProvider = ({ children }) => {
const [expenseType, setExpenseType] = useState();
const [description, setDescription] = useState();
const [value, setValue] = useState();
return (
<ExpenseContext.Provider
value={{
expenseType,
description,
value,
setExpenseType,
setDescription,
setValue,
}}
>
{children}
</ExpenseContext.Provider>
);
};
Where I want to render
import React from "react";
import { useContext } from "react";
import { ExpenseItem } from "./ExpenseItem";
import { ExpenseContext } from "../ExpenseContext";
export const ExpenseList = () => {
const context = useContext(ExpenseContext);
return (
<div>
{Object.keys(context).map((key) => (
<ExpenseItem
expenseType={key.expenseType}
description={key.description}
value={key.value}
></ExpenseItem>
))}
</div>
);
};
My item component
In my item component I'm just rendering {props.description} as a test.
Context data
My context gets data from a form like this:
import React from "react";
import classes from "./Form.module.css";
import { IoIosAddCircleOutline } from "react-icons/io";
import { useContext, useState } from "react";
import { ExpenseContext } from "../ExpenseContext";
export const Form = () => {
const [errorDesc, setErrorDesc] = useState(false);
const [errorSelect, setErrorSelect] = useState(false);
const [errorVal, setErrorVal] = useState(false);
const context = useContext(ExpenseContext);
const onSubmit = (e) => {
e.preventDefault();
if (!context.expenseType) {
setErrorSelect(true);
} else {
setErrorSelect(false);
}
if (!context.description) {
setErrorDesc(true);
} else {
setErrorDesc(false);
}
if (!context.value || isNaN(context.value)) {
setErrorVal(true);
} else {
setErrorVal(false);
}
};
return (
<form className={classes.form} onSubmit={onSubmit}>
<select
className={classes.select}
name="select"
id=""
onChange={(e) => context.setExpenseType(e.target.value)}
>
<option value="" disabled selected>
Expense Type
</option>
<option value="Savings">Savings</option>
<option value="Investments">Investments</option>
<option value="Expenses">Expenses</option>
</select>
<label htmlFor="select">
{errorSelect ? "Please choose an expense type!" : ""}
</label>
<h2 className="">Tracking Expenses</h2>
<hr />
<div className={classes.wrapper}>
<div className={classes.formGroup}>
<input
type="text"
placeholder="Description"
name="description"
className={
errorDesc ? classes.descriptionError : classes.description
}
onChange={(e) => context.setDescription(e.target.value)}
/>
<label htmlFor="description">
{errorDesc ? "Please add a description!" : ""}
</label>
</div>
<div className={classes.formGroup}>
<input
type="text"
placeholder="Amount"
name="value"
className={errorVal ? classes.valueError : classes.value}
onChange={(e) => context.setValue(e.target.value)}
/>
<label htmlFor="value">
{errorVal ? "Please add a valid value!" : ""}
</label>
</div>
<div className={classes.formGroup}>
<input type="submit" className={classes.submit} />
</div>
</div>
</form>
);
};
When you're mapping over Object.keys(context), what you're actually mapping over is ["expenseType","description","value","setExpenseType","setDescription","setValue"], because these are the keys of the object your context provides
Because those are strings, key.expenseType and key.description are undefined.
From what I have read I am pretty sure you're trying to do something else, you probably want your context to provide an array of objects which you map over, something like
import { createContext, useState } from "react";
export const ExpenseContext = createContext();
export const ExpenseProvider = ({ children }) => {
const [expenses, setExpenses] = useState([]);
return (
<ExpenseContext.Provider
value={{
expenses, setExpenses
}}
>
{children}
</ExpenseContext.Provider>
);
};
Where the members of expenses are object with the properties of expenseType, description and value, am I correct?

Set state from props change in react functional component?

I have simple react functional component looks like this following code
import React, { useState } from "react";
import "./styles.css";
export default function App() {
return (
<div className="App">
<Test
children={
<React.Fragment>
<label>Name: </label>
<input type="text" onChange={(e) => setData(e)} />
</React.Fragment>
}
/>
</div>
);
}
export function Test({ children }) {
const [data, setData] = useState("");
return (
<>
<div>{children && children}</div>
<div>{data}</div>
</>
);
}
My question is how to update data state inside Test Component when onChange event triggered?
Here is the sandbox
Hopefully anyone can help me..
Thanks in advance
You dont have to send children that way. React has a specific way of handling children which is more easier to do and maintain. In your case you just have to lift you state up and send the state and callback as props.
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [data, setData] = React.useState('')
return (
<div className="App">
<Test data={data}>
<> // shorthand for ReactFragment
<label>Name: </label>
<input type="text" onChange={(e) => setData(e.target.value)} value={data}/>
</>
</Test>
</div>
);
}
export function Test({ data, children }) {
return (
<>
<div>{children && children}</div>
<div>{data}</div>
</>
);
}
You can use use render props technique for this and you can can keep your pattern intact if you do not prefer major refactoring.
Working Demo - https://codesandbox.io/s/flamboyant-monad-hw2lu?file=/src/App.js
import React, { useState } from "react";
import "./styles.css";
export function Child(props) {
const { setData } = props;
return (
<React.Fragment>
<label>Name: </label>
<input type="text" onChange={(e) => setData(e.target.value)} />
</React.Fragment>
);
}
export default function App() {
return (
<div className="App">
<Test
render={(props) => {
const { setData } = props;
return <Child setData={setData} />;
}}
></Test>
</div>
);
}
export function Test(props) {
const [data, setData] = useState("");
const { render } = props;
return (
<>
<div>{render({ setData })}</div>
<div>{data}</div>
</>
);
}

react-hook-form - Cannot pass form data to a parent component

I'm using react-hook-form and trying to pass the data from a form to its parent.
For this I'm trying to pass the data via a callback, but it's not working.
When I console.log data inside the parent component, I get undefined.
Parent component
import React from 'react';
import InputForm from './InputForm';
const App = (data) => {
const onFormSubmit = () => {
console.log(data.name);
};
return (
<div className='App'>
<InputForm onceSubmited={() => onFormSubmit()} />
</div>
);
};
export default App;
Child component
import React from 'react';
import { useForm } from 'react-hook-form';
const InputForm = ({ onceSubmited }) => {
const { register, handleSubmit } = useForm();
const onSubmit = (data) => {
console.log(data);
onceSubmited(data);
};
return (
<>
<form onSubmit={handleSubmit(onSubmit)}>
<input
type='text'
name='name'
ref={register}
/>
<input
type='email'
name='email'
ref={register}
/>
<button type='submit'>
Submit
</button>
</Form>
</>
);
};
export default InputForm;
You need to pass the argument in your arrow function. This should make it work:
import React from 'react';
import InputForm from './InputForm';
const App = () => {
const onFormSubmit = (data) => {
console.log(data.name);
};
return (
<div className='App'>
<InputForm onceSubmited={(data) => onFormSubmit(data)} />
</div>
);
};
export default App;

Resources