Passing function down multiple children reactJS - javascript

I'm trying to build an application to help me learn react. It's a simple app that takes users input location, gets the coordinates from googles geolocation api and then gets the weather conditions for that location with dark skys api.
I originally had all my application logic in one container which you can see here https://github.com/darragh3277/night-sky
I want to separate out my logic a bit more to as I felt my one container was doing too many things. I'm looking to structure my app as follows.
App.js - Holds state, locationChangeHandler function to pass down to LocationSearchContainer.
LocationSearchContainer - Geolocation API and calls LocationSearch to display search bar
WeatherContainer - WeatherAPI and calls Weather component to render the display.
I believe this gives me a better separation of concerns and makes my code a bit more readable.
Because of this structure I think I need a locationChangeHandler in my App.js that will be passed to my Dump LocationSearch component. This tightly couples my App.js to the search bar but as that's the core function of the app I don't believe it's a problem.
What I'm having trouble with is how to pass my handler from App.js -> LocationSearchContainer -> LocationSearch. Here is my poor attempt so far:
In App.js I have
handleLocationChange = e => {
e.preventDefault();
console.log('hello');
//Calls getLocation in LocationSearchContainer and updates state
}
render(){
return (
<LocationSearchContainer onLocationChange={this.handleLocationChange} />
)
}
In LocationSearchContainer I have the following:
import React from 'react';
import LocationSearch from '../components/LocationSearch';
class LocationSearchContainer extends React.Component{
getLocation = (address) => {
//DO STUFF
}
render(){
return (
<LocationSearch onLocationChange={this.props.handleLocationChange} />
)
}
}
export default LocationSearchContainer;
And finally LocationSearch:
import React from 'react';
const LocationSearch = (
<form onSubmit={props.onLocationChange}>
<div className="input-group">
<input type="text" name="location" className="form-control" placeholder="Search location..." />
<button className="btn btn-primary">Go!</button>
</div>
</form>
)
export default LocationSearch;
How can I pass this handler down correctly?
Once I have the above working I'll also need to call the getLocation in my SearchLocationContainer from App.js but I'm not sure how to do that either?
EDIT
I've figure out part one of my problem. I was calling the function by name rather than the prop name I was passing. So in the render() function of my LocationSearchContainer I should have had
return (
<LocationSearch onLocationChange={this.props.handleLocationChange} />
)
rather than my original.
That still leaves me with the problem of calling my LocationSearchContainer's getCoordinates function from my App.js file. Anyone able to help me with that?

You are passing your handler like that:
<LocationSearchContainer onLocationChange={this.handleLocationChange} />
So, in LocationSearchContainer component, your handler function's name is onLocationChange, within props it is this.props.onLocationChange.
So, you need to pass this function to your LocationSearch component like that:
<LocationSearch onLocationChange={this.props.onLocationChange} />
Lastly, you need to use it in LocationSearch as props.onLocationChange
For your second question, you shouldn't try to invoke a child component's method from the parent. Change your logic. Either keep this function in the parent, then pass it again as a prop to your child component or move the logic to your child component.

Related

How do I use React Navigation to send parameters to another screen without moving there?

I know that you can do navigation.navigate("address", {/* params go here */ to send parameters over to another screen. But then you have to navigate there. Is there a way of sending params over without navigating?
I have a application with multiple screens. And I want to update a useState from another component by updating its params so that a button appears. But I dont want to navigate there, I just want to update it so when the user does go there the button will be there.
Like this:
const currentComponent = (navigation) {
return (
<Button onPress={navigation.updateParams("otherComponent", {shouldShowValue: true})} />
)
}
const otherComponent = (route, navigation) {
const {shouldShowValue} = route.params
const [shouldShow, setShouldShow] = useState(shouldShowValue);
return (
{shouldShow ? <Button> Yayy this button appears now <Button /> : null}
)
}
}
'''
this is just pseudo code and not at all
like the code I have written,
but its just meant as an example to get a
understanding of what I mean.
(updateParams) isnt a function that exists,
but I want something similiar like it.
Is there a way of updating the params in a
component from another component without having
to navigate there? Like with
navigate.navigate("address" {params go here})
but without the navigation part?
You can consider using useContext() hook to execute your functionality.
Using navigation library to pass param without navigating to that page is somehow misusing the navigation function.
With useContext, you can share the state(s) among components. If you want to change the value upon clicking action, you can also pass the useState hook into useContext. Alternatively, you can consider to use redux library to share state.
import { useState, createContext, useContext } from 'react';
const shareContext = createContext(null);
export default function demoUseContext() {
const [isClicked, setClicked] = useState(false);
return (
<shareContext.Provider value={{isClicked, setClicked}}>
<ComponentA />
<ComponentB />
</shareContext.Provider>
)
}
function ComponentA() {
const sharedParam = useContext(shareContext);
return (
<button onClick={() => sharedParam.setClicked(!sharedParam.isClicked)}>
click to change value
</button>
);
}
function ComponentB() {
const sharedParam = useContext(shareContext);
return (
sharedParam.isClicked && <div>it is clicked</div>
)
}
As the example above, the code pass the useState hook from parent component into context, where A is consuming the useState from context to setup isClicked via setClicked, B is consuming the value isClicked from context.
You can also manage to setup context with value not only in a hook, but a param / object / function as a callback.
For more details, please refer to https://reactjs.org/docs/hooks-reference.html#usecontext
There're multiple hooks including useContext fyi
Passing parameters to routes
There are two pieces to this:
Pass params to a route by putting them in an object as a second parameter to the navigation.navigate function: navigation.navigate('RouteName', { /* params go here */ })
Read the params in your screen component: route.params.
We recommend that the params you pass are JSON-serializable. That way, you'll be able to use state persistence and your screen components will have the right contract for implementing deep linking.

Passing useState state

How can I extract the useState from the file. It is necessary to export exactly sliderCount for further work. Thanks in advance.
There are 2 files. The first file uses the useState function and changes the state when the button is clicked. In the second file, I want to get the changed state of useState from the first file and continue to work with it.
Make conditions, etc., for example:
if(sliderCount == 1){
console.log('Number 1')
}
if(sliderCount == 2){
console.log('Number 2')
}
First file:
import React, {useState} from "react";
function ContainerAtwSwap() {
const [sliderCount, setSliderCount] = useState(0)
return (
<div className="container_button__swap-main">
<div className="dropdown">
<div className="dropdown-content">
<button className="button__swap-button" onClick={() => setSliderCount(1)}>№1</button>
<button className="button__swap-button" onClick={() => setSliderCount(2)}> №2</button>
<button className="button__swap-button" onClick={() => setSliderCount(3)}>№3</button>
</div>
</div>
</div>
)
}
export default ContainerAtwSwap
Second file:
import React from 'react';
function WorkingEnvironment() {
..here I wanted to make conditions, etc.
return (
<div className='boxControl scroll' id='boxSliderOne'>
<div className='container-slider'>
{}
</div>
</div>
);
}
export default WorkingEnvironment;
That is what the Context API is for: https://reactjs.org/docs/context.html
Of course you could also use Redux, or transfer data from child to parent and to another child, but the Context API solves this in a much easier way and it is built in without any extra dependency.
One way is to have a parent (of both components) relay the change to the other. Component A will send a changed event to the parent (via a component property event). Then the parent will simply send that changed value to Component B (via a component property).
Grouping your components in one parent component and lifting the state up to their parent is the only way you can share their state.
Otherwise you have to implement a redux store or anything like that.
I link you the React doc's below
https://reactjs.org/docs/lifting-state-up.html

Separating the logic and the render() function from a component

I would like to be able to have a component whose rendering function is in another file in order to have a separation between the logic of my component and the rendering.
Naively, I tried to do just one file containing my component and which rendered a functional component of the same name to which I passed the necessary props so that everything was displayed correctly.
Something like that :
// MyComponent.render.jsx
export default MyComponentRender = (props) => {
return {
<View>
// render all components of my view
</View>
}
}
// MyComponent.js
class MyComponent extends Component {
// some logic
render() {
return (
<MyComponentRender
aLotOfProps=....
/>
)
}
}
But I soon found myself having to send, sometimes, a fairly large amount of props and +, I have for example textInputs that need to be focus() or blur() in reaction to some logic in my view but as a result, I couldn't control that just by sending props. It quickly became a mess!
I was wondering if there was a simple way to separate the logic of a component and its rendering function? Maybe there is a way to pass the context of my component to my rendering function/component so that it has direct access to all states and can also store references, etc.?
Thanks you,
Viktor

How a component pass a prop to another component?

Newbie here, I am studying the documentation of react and in React Context API, I couldn't understand something, I won't understand the rest of the subject if I don't understand it. Can anyone help me what does it mean through using an example?
The Toolbar component must take an extra "theme" prop
and pass it to the ThemedButton. This can become painful
if every single button in the app needs to know the theme
because it would have to be passed through all components.
class App extends React.Component {
render() {
return <Toolbar theme="dark" />;
}
}
function Toolbar(props) {
// The Toolbar component must take an extra "theme" prop
// and pass it to the ThemedButton. This can become painful
// if every single button in the app needs to know the theme
// because it would have to be passed through all components.
return (
<div>
<ThemedButton theme={props.theme} />
</div>
);
}
class ThemedButton extends React.Component {
render() {
return <Button theme={this.props.theme} />;
}
}
The Toolbar component must take an extra "theme" prop
this can be like <Toolbar theme="dark">
and pass it to the ThemedButton
how Toolbar component pass this prop to ThemedButton? and kindly clarify the rest of the comment as well.
Thank you for any help? You are kind
In your Toolbar component, it takes a parameter props, props is whatever properties have been passed to it when calling it, as in <Toolbar param1="someString" param2={someVariable}>, in this case the props value in Toolbar will be an object with the data you passed as key=value like for example: {param1: "someString", param2: content_of_someVariable}
And if you don't actually use those props (properties)/parameters in Toolbar, but rather in a subcomponent, then you have to pass them again to another level, like in <ThemedButton theme={props.theme} />, then ThemedButton itself finally passes the value to the component that actually makes use of, which is in your case: <Button theme={this.props.theme} />;.
So you had to pass the theme across multiple components, which don't use it or care at all about it, just to get it through to the final Button component.
(answer ends here, below is my effort to explain context API in an easy way)
To avoid that annoying level to level to another..., you can use the context API. Because it is really incontinent to pass a value across 3-4+ levels/components every time you want to use it in the last one in the chain.
Think about the context like a variable defined and exported on a root level and holds some data (like the user login status for example, or the theme infomation), and whenever you require that data, you import it and use it directly. You use the Provider property of the context you define (MyContext.Provider) to assign the data to it, and you use the Consumer property (MyContext.Consumer) to consume/access that data you assigned in the provider.
The beauty of the context consumer, is that whenever the data is updated in the provider, the consumer immediately gets the new data and triggers a re-render with the new data.
I hope I explained it in a simple and clear way. Write a comment with any questions or unclear parts and I can try my best to improve the answer.
Best of luck!
Props are properties that help define the way your JSX appears on the page.
When you use a component that you have created, you can pass it props like below:
<MyComponent myProp={myPropValue} />
You can continue to pass props down through the component hierarchy as well. So say you have a component tree like below:
MyComponent
--MySubComponent
----MySubSubComponent
You can pass props from MyComponent to MySubSubComponent like so:
<MyComponent myProps={MyProps} />
<MySubComponent mySubProps={props.myProps} /> //Props are the value you gave in the parent component
<MySubSubComponent mySubSubProps={props.mySubProps} />
Whatever title you give the props when declaring the component in JSX is the title you will call to get the value of the prop like props.myProps

How to render results of search in another component in React?

I am a beginner in React and using Webpack to build into a bundle.js and display.
My need is to provide some search forms and accordingly display result below search form. So, for modularizing it, I have create a parent component containing both search and result view components.
Now that I have designed a form and written form onSubmit event handler, how should i proceed to render API results (dummy json for now) in the result component. I am attaching a brief pic of my intention for your reference.
Here is my solution based on my comments above: https://codesandbox.io/s/q85oq0w10q
Create an HOC that will hold the state of your app, then your two children are merely used for rendering purpose and can be made pure functions
import React from 'react';
import { render } from 'react-dom';
const Result = ({results}) => {
return results.map((r, i) => <div key={i}>{r}</div>);
}
const Search = (props) => {
const {
searchQuery,
onChange,
search
} = props;
return <div>
<input
type="text"
value={searchQuery}
onChange={onChange}
/>
<button onClick={search}>Search</button>
</div>;
}
class Container extends React.Component {
constructor(props) {
super(props);
this.state = {
searchQuery: '',
results: []
}
this.onSearchQueryChange = this.onSearchQueryChange.bind(this);
this.onSearch = this.onSearch.bind(this);
}
onSearchQueryChange(e) {
this.setState({searchQuery: e.target.value});
}
onSearch() {
// Simulate AJAX call
setTimeout(() => {
this.setState({results: [0, 1, 2, 3, 4]});
}, 1000)
}
render() {
const {results, searchQuery} = this.state;
return <div>
<Search
searchQuery={searchQuery}
onChange={this.onSearchQueryChange}
search={this.onSearch}
/>
<Result results={results} />
</div>;
}
}
I believe this is what you are looking for. Worked example fiddle
So the idea is to keep your result in Container component and pass it down to Result component. Also Container component should pass a callback function to your Search component and it will be triggered with a API result from the Search component.
Also you may want to take a look at Redux. This is a library for managing your app state. With Redux it can be achieved in more easiest way.
Hope it will help you.
In my opinion if you are new in React. You should learn first using React. Because I see that a lot of people use Redux(or any other app state handler) as a storage for any data.
Your case is actually very good example to learn two of the basic ideas: if children need similar thing, parents should handle it and params go down and callbacks go up.
So all your logic has to be in Container Component, make callback of http request function, with update of state(setState) after resolving response and send to Search Component. onSubmit call that callback, and send data to Result Component.
So you no need of additional library(maybe for http request).
1 Class component(Container Component). 2 Probably stateless functional components(Search Component & Result Component).

Categories