Call a method from another Component after the Async function reactjs - javascript

I have 2 components, the first component has a function that calls after the async function of the second component, what I want to do is something like vue's this.$emit() function that calls a listener from that component anytime, how can I do that in react?
This is my first component
import React, { Component } from 'react';
import SecondComponent from '../Path/to/second/component'
class MainMenu extends Component {
callThis (data) {
console.log(data)
}
render () {
return <SecondComponent onDataReceived = {this.callThis} />
}
}
export default FirstComponent
And this is my SecondComponent
import React, { Component } from 'react';
class SecondComponent extends Component {
async asyncFunction () {
const data = await getDataFromApi()
// call the function from first component here...
}
render () {
return <button onClick={() => this.asyncFuncion} />
}
}
export default FirstComponent

Your second component must invoke asyncFuncion, and then inside asyncFuncion you can call the callThis function from the props
class SecondComponent extends Component {
async asyncFunction () {
const data = await getDataFromApi()
this.props.onDataReceived(data)
}
render () {
return <button onClick={() => this.asyncFuncion()} />
}
}
and do not forget to bind that callThis as well, or just use fat arrow function:
class MainMenu extends Component {
callThis = (data) => {
console.log(data)
}

On your first component, you are sending a props to your second components.
Here is the documentation : https://reactjs.org/docs/components-and-props.html
To access onDataReceived in your second component you could write :
async asyncFunction () {
const data = await getDataFromApi()
this.props.onDataReceived(data);
}

this is how you can receive data/use methods from parent passed props:
async asyncFunction () {
const data = await getDataFromApi()
// call the function from first component here...
this.props.onDataReceived(data);
}

Related

How to re-render parent component on child component's button click

I am learning react and I would still consider myself to be a beginner. My goal is to click a button on my child component so that I could re render the parent component. This is the code I have.
Parent Component
import React, { Component } from 'react';
import Activity from './Components/Activity'
class App extends Component {
state = {
activity: ''
}
handleClick = () => {
// I have read that forceUpdate is discouraged but this is just an example
this.forceUpdate()
}
async componentDidMount() {
const url = 'http://www.boredapi.com/api/activity/'
const response = await fetch(url);
const data = await response.json();
this.setState({
activity: data.activity
})
}
render() {
return (
<div>
<Activity act={this.state.activity} click={this.handleClick}/>
</div>
);
}
}
export default App;
Child Component
import React, { Component } from 'react';
class Activity extends Component {
render() {
return (
<div>
<h1>Bored? Here is something to do</h1>
<p>{this.props.act}</p>
<button onClick={this.props.click}>Something Else</button>
</div>
);
}
}
export default Activity;
As you can see I am trying to click a button so that I could get another fetch and a different activity renders on my child component. I am trying to keep my child component stateless but if keeping it stateless doesn't make sense or is just plain wrong I would love to know.
You can try to move fetching function outside componentDidMount
for the example:
handleClick = () => {
this.fetchdata();
}
async fetchdata(){
const url = 'http://www.boredapi.com/api/activity/'
const response = await fetch(url);
const data = await response.json();
this.setState({
activity: data.activity
})
}
componentDidMount() {
this.fetchdata();
}
You can make a class method for fetching the new activity,
Call it after the app first mounted with componentDidMount() and again when you call it from the child component Activity.
You should mentioned in the your question that the response body is different in each request you make.
import React, { Component } from 'react';
import Activity from './Activity'
class App extends Component {
state = {
activity: ''
}
handleClick = () => {
this.getActivity()
}
componentDidMount() {
this.getActivity();
}
async getActivity() {
const url = 'https://www.boredapi.com/api/activity/'
const response = await fetch(url);
const data = await response.json();
this.setState({
activity: data.activity
})
}
render() {
console.log(this.state);
return (
<div>
<Activity act={this.state.activity} click={this.handleClick}/>
</div>
);
}
}
export default App;
Here is also a sandbox:
https://codesandbox.io/s/dreamy-noether-q98rf?fontsize=14&hidenavigation=1&theme=dark

GlobalContext is not update in class component

I'm trying to create GlobalContext but when I update this inside Class Component it didn't work in Class Component it's showing value of globalState but it's not updating globalState via setGlobalState
GlobalContext
import React, { useState ,ReactNode} from 'react'
const initialMapContext: { globalState: any; setGlobalState: React.Dispatch<React.SetStateAction<any>> } = {
globalState: {},
// will update to the reducer we provide in MapProvider
setGlobalState: () => {},
};
const GlobalContext = React.createContext(initialMapContext );
interface Props { children?: ReactNode;}
export function GlobalProvider({children}:Props){
const [ globalState, setGlobalState ] = useState({name:'pranjal'});
return <GlobalContext.Provider value={{ globalState, setGlobalState }}>{children}</GlobalContext.Provider>;
}
export default GlobalContext
my code in classComponent is
static contextType = GlobalContext;
getData = async () =>{
const { globalState, setGlobalState } = this.context;
console.log(globalState); // pranjal
setGlobalState({name:"please login"});
console.log(globalState); // pranjal
// my rest code
}
but setGlobalState is not updating globalState value .
Although it works fine in the Functional component
Function.js
const { globalState, setGlobalState } = useContext(GlobalContext);
setGlobalState({name:'Please login'})
Rather than using static contextType = GlobalContext; , I would recommend you to use a Higher Order Component (HOC) which wraps a component with your GlobalContext.
Impementation:
GlobalContext
Export one HOC method called withGlobalContext as follows,
export const withGlobalContext = (Component) => (props) => (
<GlobalContext.Consumer>
{({ globalState, setGlobalState }) => (
<Component
globalState={globalState}
setGlobalState={setGlobalState}
{...props}
/>
)}
</GlobalContext.Consumer>
)
ClassComponent
Wrap the component with the HOC, to get the global context values as the props. And being available in the props, you can use it anywhere in the component, even outside render()
class ClassComponent extends Component {
componentDidMount() {
console.log(this.props.globalState)
console.log(this.props.setGlobalState)
}
render() {
return (
// Your JSX here
)
}
export default withGlobalContext(ClassComponent)
Also, I prefer exporting a custom hook, for using context in functional components, rather than using useContext
Implementation:
export function useGlobalContext() {
const context = useContext(GlobalContext)
if (context === undefined) {
throw new Error('You did something wrong')
}
return [context.globalState, context.setGlobalState]
}
Then in your functional component, use it like following:
function FunctionalComponent(){
const [globalState, setGlobalState] = useGobalContext()
return (
// Your JSX here
)
}
Cheers!

How to get values coming from an external file with a class component?

I am getting values from locaStorage through this file:
const cartDataFn = () => {
let cartData = []
const cartDataStorage = JSON.parse(localStorage.getItem('cartList'))
if (cartDataStorage !== null) {
cartData = cartDataStorage
}
return cartData
}
export default cartDataFn
OUTPUT:
As you see it is a function which returns an array (cartData)
If I import it into a function component I am able to get the array. Like this:
import React, { Fragment } from 'react'
import cartData from '../../../Data/cartData';
const Products = props => {
let [cartState, setCart] = React.useState(cartData)
console.log(cartState) // => It prints the array cartData
}
export default Products
However, when I try to do the same with a class component I don't know how to get cartData. If I log the file into console I get a visual represention of the function.
import React, { Component } from 'react'
import cartData from '../../Data/cartData'
class Layout extends Component {
state = {
cartState: cartData,
}
componentDidMount(){
console.log(this.state.cartState) // => It wont give me 'cartData', rather I'll get a visual draw of the whole function
}
render () {
return (
<div>
<p>Anything</p>
</div>
)
}
}
export default Layout
OUTPUT:
How can I access these values in class components?
Since cartDataFn is a function, you need to invoke it in the class component:
class Layout extends Component {
state = {
cartState: cartData(),
}
(preferably, give it a more accurate name to indicate that it's a function, not data)

Trigger setState function in parent from promise.then in child

I am trying to find a solution to setState from a parent within child promise.
The parent component is
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
transition: false
};
}
handleTransition = () => {
this.setState(state => ({ transition: !state.transition }));
};
render() {
return <Child handleTransition={this.handleTransition} />;
}
}
of which this.props.handleTransition is to be triggered from a child component as
class Child extends Component {
constructor(props) {
super(props);
this.state = {};
}
onSubmit = event => {
firebase
.doCreateUserWithEmailAndPassword(email, password)
.then(() => {
// Trigger this.props.handleTransition here
})
...
Where this.props.handleTransition is wanting to be triggered with then of onSubmit
Please let me know if you require more detail? I would prefer not to use a library or package to achieve this but if it makes life easier I may consider. Redux is likely the best option but I would prefer not to unless necessary.
Note: this.props.handleTransition(); does the job but esLint returns an error of Must use destructuring props assignmenteslint(react/destructuring-assignment) and I am considering that this method is not the correct method.
// --- parent.js
import React, { Component, Fragment } from "react";
import { ChildComponent } from './containers/child'
class ParentContainer extends Component {
handleUpdate = () => {
// whatever you want to do here
}
render(){
return (
<Fragment>
<ChildComponent onUpdate={this.handleUpdate} />
</Fragment>
);
}
}
export default ParentContainer;
// --- child.js
import React, { Component, Fragment } from "react";
export class ChildComponent extends Component {
this.someAsyncFunction = () => {
fetch('/just/for/example')
.then(res =>
// Do whatever you need here, then hit your function on parent by bubbling the request up the chain
this.props.onUpdate();
)
}
render(){
return (
// whatever you want to do with this data
);
}
}

React Native pass function to child component as prop

I'm new to React Native (and React), and I'm trying to pass a function as a prop to a component.
My goal is to create a component where its onPress functionality can be set by the instantiator of the component, so that it is more reusable.
Here is my code so far.
App.js
import React, { Component } from 'react';
import { View } from 'react-native';
import TouchableButton from './components/touchable-button';
export default class App extends Component<Props> {
constructor () {
super();
}
handlePress () {
// this should be called when my custom component is clicked
}
render () {
return (
<View>
<TouchableButton handlePress={this.handlePress.bind(this)}/>
</View>
);
}
}
TouchableButton.js
import React, { Component } from 'react';
import { TouchableHighlight } from 'react-native';
import AppButton from "./app-button";
export default class TouchableButton extends Component {
handlePress;
constructor(props){
super(props);
}
render () {
return (
<TouchableHighlight onPress={
this.props.handlePress
}>
<AppButton/>
</TouchableHighlight>
);
}
}
I am passing the handlePress function as the prop handlePress. I would expect the TouchableButton's props to contain that function, however it isn't there.
Solution
Use arrow function for no care about binding this.
And I recommend to check null before calling the props method.
App.js
export default class App extends Component<Props> {
constructor () {
super();
}
handlePress = () => {
// Do what you want.
}
render () {
return (
<View>
<TouchableButton onPress={this.handlePress}/>
</View>
);
}
}
TouchableButton.js
import React, { Component } from 'react';
import { TouchableHighlight } from 'react-native';
import AppButton from "./app-button";
export default class TouchableButton extends Component {
constructor(props){
super(props);
}
handlePress = () => {
// Need to check to prevent null exception.
this.props.onPress?.(); // Same as this.props.onPress && this.props.onPress();
}
render () {
return (
<TouchableHighlight onPress={this.handlePress}>
<AppButton/>
</TouchableHighlight>
);
}
}
When writing handlePress={this.handlePress.bind(this)} you passing a statement execution ( which when and if executed returns a function). What is expected is to pass the function itself either with handlePress={this.handlePress} (and do the binding in the constructor) or handlePress={() => this.handlePress()} which passes an anonymous function which when executed will execute handlePress in this class context.
// Parent
handleClick( name ){
alert(name);
}
<Child func={this.handleClick.bind(this)} />
// Children
let { func } = this.props;
func( 'VARIABLE' );

Categories

Resources