How to get React props outside component - javascript

I need a way to load the correct language of this script, and that info is a props value. Roughly put, it would look something like this:
class AddressInput extends React.Component {
render() {
return (
<PlacesAutocomplete
do={this.someStuff}
/>
);
}
}
export default scriptLoader(
`https://maps.googleapis.com/maps/api/js?&language=${this.props.language}`;
)(connect(mapStateToProps, mapDispatchToProps)(AddressInput));
I understand that this.props isn't accessible outside the component, so how would I be able to get scriptLoader to get a dynamic value?

You're basically there. You even have the idea of making it an HOC. Here is the impelementation:
function scriptLoader(WrappedComponent) {
return (props) => {
const dynamicUrl = `https://maps.googleapis.com/maps/api/js?&language=${props.language}`;
return <WrappedComponent {...props} url={dynamicUrl}/>
}
}
The HOC is accepting a prop called language, creates the url, and passes it down to the wrapped component, under the prop name url. Obviously, all these prop names can be changed at your discretion.
// usage in the parent
<AddressInput language="en"/>
// what is available in your presentational component
class AddressInput extends React.Component {
render() {
console.log(this.props.url) // https://maps.googleapis.com/maps/api/js?&language=us
return (
<PlacesAutocomplete
do={this.someStuff}
/>
);
}
}

Related

Spreading props in React Higher Order Components

I am trying to go very in-depth to understand the purpose of spreading props in React HOC
So taking the below example;
const EnhanceComponent = BaseComponent => {
return class EnhancedComponent extends Component {
state = {
name: 'You have been enhanced'
}
render() {
return (
<BaseComponent {...this.props} {...this.state} />
)
}
}
};
export default EnhanceComponent;
Now let's say the usage of BaseComponent is as below;
<BaseComponent className='wrapper-container' onClick={this.handleClick} />
I assume if had not spread the props in the HOC, we would have been unable to access "this.props.className" OR "this.props.onClick" in BaseComponent. Would that be correct understanding ?
class BaseComponent extends React.Component {
render() {
const { className, onClick} = this.props;
...
}
}
Now to use the HOC itself, we would say;
const EnhancedMyComponent = EnhanceComponent(MyComponent);
And render it as
<EnhancedMyComponent className='wrapper-container' onClick={this.handleClick} />
Now, below are my 2 specific questions;
What do we finally render i.e. BaseComponent or EnhancedMyComponent OR using HOC allows us to use either flavor e.g. in some case, if we do not want the enhanced functionality, we just use the base component ?
OR
<EnhancedMyComponent className='wrapper-container' onClick={this.handleClick} />
Would the props access issue i.e. if we do not spread the props be applicable in both the above cases of consumption i.e. <BaseComponent /> AND <EnhancedMyComponent /> ?
1) What do we finally render i.e. BaseComponent or EnhancedMyComponent OR using HOC allows us to use either flavor e.g. in some case, if we do not want the enhanced functionality, we just use the base component ?
/ Using HOC allows us to use either flavor. It totally depends where we are wrapping the Component in HOC i.e while exporting or while using it at someplace.
Now, In the below case one has the option to use it with or without HOC
// BaseComponent.js
class BaseComponent extends React.Component {
render() {
const { className, onClick} = this.props;
...
}
}
export default BaseComponent;
// SomeComponent.js
import BaseComponent from './BaseComponent';
const MyComponent = EnhanceComponent(BaseComponent);
class SomeComponent extends React.Component {
render() {
return (
...
<MyComponent className={...} onClick={...} someExtraPropForHOC={...}/>
<BaseComponent className={...} onClick={...} />
...
)
}
To not allow anyone to directly use the Component, wrap it in HOC and export
// BaseComponent.js
class BaseComponent extends React.Component {
render() {
const { className, onClick} = this.props;
...
}
}
export default EnhanceComponent(BaseComponent);
// SomeComponent.js
import BaseComponent from './BaseComponent';
class SomeComponent extends React.Component {
render() {
return (
...
<BaseComponent className={...} onClick={...}/>
...
)
}
2) Would the props access issue i.e. if we do not spread the props be applicable in both the above cases of consumption i.e. AND ?
/ Spread the props is needed as HOC does not know what props would be needed for the dynamically wrapped component. So pass all the props which are coming is the only possible way.
class BaseComponent extends React.Component {
render() {
const { className, onClick} = this.props;
...
}
}
class CustomTextField extends React.Component {
render() {
const { className, onKeyPress, value} = this.props;
...
}
}
const EnhancedBaseComponent = EnhanceComponent(BaseComponent);
const EnhancedTextComponent = EnhanceComponent(CustomTextField);
Now in this case EnhancedBaseComponent and EnhancedTextComponent both need different props, but since they are wrapped in EnhanceComponent. It won't know which props to pass. So spread it and send all the props coming to it.

How can I pass attributes from a stateful component to an event handler used in an HOC wrapping a child component?

I am using a framework where I must pass an event handler into an HOC that is wrapping one of the children of my stateful Page component.
<Page>
<HOC onClick={fn}>
<PageColumn>
</PageColumn>
</HOC>
</Page>
I have a function in my stateful Page component that relies on the state and props of page. I must use a HOC that wraps a child component of the page. Within this HOC I must call an onClick method that relies on the state of the Page component.
So far I have tried passing down a reference to this.state in order to use the Page state in the HOC, and I have tried passing down props that are assigned the values that I needed in the parent state. In the onClick fn, whether I reference the necessary properties by using this.state or this.props, I get the error:
cannot read property '<attribute>' of undefined
How can I successfully implement this pattern?
There is not a lot to investigate from you code.
I noted that you use HOC as a component, but usually hoc are functions that add something to a component.
Usually hoc works like this:
EnrichedComponent = hoc(options)(BaseComponent);
example: react-redux connect hoc function
This approach should work:
// render Page somewhere
const App = () => (
<Page/>
)
// let Page component render the PageColumn component
class Page extends React.Component {
handleClick() {
// I can access page state and props
const {
state: { foo },
props: { bar },
} = this;
console.log(foo, bar);
}
render() {
return (
<PageColumn onClick={this.handleClick} />
)
}
}
// wrap component with HOC
const PageColumn = hoc()(() => {
return (
<div>...</div>
)
});
Take a look at the code below. Basically, you want to create the function in your top level component and bind it to this in the constructor. Then pass it down as a property to your child component (i.e. no parameter list, no function syntax, just `this.myFunc').
In the child component, in the onClick event, call the function and pass in your parameters. Use the name of the attribute of the parent function to call the function. So for example, here it's still this.props.myFunc, but if <Page /> had this expression instead: mySpecialFunc={this.myFunc} you would call like this instead <HOC onClick={(prm1,prm2,prm3) => this.props.mySpecialFunc(prm1,prm2,prm3)} />
In <Page />:
constructor(){
//regular constructor code
this.myFunc = this.myFunc.bind(this);
}
myFunc(prm1, prm2, prm3){\
//function code
//
this.setState({
myFuncState: "newValue",
//additional code
});
}
render(){
return (
<Page>
<HOC myFunc={this.myFunc}>
<PageColumn />
</HOC>
</Page>
)
}
In <HOC />:
render(){
prm1 = "someValue1";
prm2 = "someValue2";
prm3 = "someValue3";
return (
<div className="HOC" onClick={(prm1,prm2,prm3) => this.props.myFunc(prm1,prm2,prm3)}>
)
}
A Note on Parameters:
The above code is for the situation where all of your parameters are in one place (in this case, the direct child component). Sometimes they are spread out amongst parent, child, grandchild components, etc. In that case, you need to declare the parameters generically in each component, passing in the relevant parameters as you move down the chain of component ancestry. For example:
class Parent extends React.Component{
constructor(){
//normal render code
this.passedFunc = this.passedFunc.bind(this);
}
passedFunc(prm1,prm2){
//function code
this.setState({
a: val1,
b: val2,
//etc.
})
}
render(){
return (
<Child passedFunc={this.passedFunc}
)
}
}
class Child extends React.Component{
//other relevant code
render(){
let val1 = "foo";
return (
//we're passing prm1 into the func as val1. <Child /> doesn't know what prm2 is so we're keeping that generic and letting <GrandChild /> handle it
<GrandChild passedFunc={(prm1, prm2) => this.props.passedFunc(val1, prm2)}
)
}
}
class GrandChild extends React.Component{
//other relevant code
render(){
let val2 = "bar";
return (
//<GrandChild /> doesn't know what prm1 is, but it knows that prm2 is val2, so it's passing in prm2 as val2 and leaving prm1 generic so <Child /> can handle it
<div passedFunc={(prm1, prm2) => this.props.passedFunc(prm1, val2)}
)
}
As you can see, each component passes parameters up to their parent component and uses the generic parameter name for params they don't know. Finally, the parent component receives all of the parameters and is able to use them in the function. If there was a middle man component in the chain that didn't need to set any params, for example a component named between <Child /> and '' named <InBetween />, you could simply pass the function name through that component as a simple property, like this:
<InBetween passedFunc={this.props.passedFunc} />
Any parameters set in a component above or below it in the chain will remain intact.

passing state value to a child component via props

i'm trying to pass the value entered by the user from the app component to the passTicket component. I tried invoking props to pass this state data but I keep getting an undefined error when attempting to access it. I'm new to react and it would be great if someone can help me make sense of what i'm getting wrong. This is a sample of what i'm trying to achieve.
This is my main component:
class App extends Component {
constructor(props){
super(props);
this.state = {
ticket:"",
};
this.changeTicket = this.changeTicket.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.keyPress = this.keyPress.bind(this);
}
changeTicket(e){
this.setState({
ticket : e.target.value,
})
}
handleSubmit(){
this.setState({
updatedTicket: this.state.ticket
});
}
keyPress(e){
if (e.keyCode ===13){
this.handleSubmit();
}
}
render() {
return (
<div className="App">
<header className="App-header">
<input type="text" placeholder="ENTER TICKET NUMBER" value={this.state.ticket} onKeyDown={this.keyPress} onChange={this.changeTicket}/>
</header>
</div>
);
}
}
and i'd like to be able to store the updatedTicket value in a variable which I can use in my PassTicket component. this is what i've attempted so far but the error it occurs is the following Uncaught TypeError: Cannot read property 'updatedTicket' of undefined
this is what my second component looks like:
class PassTicket extends Component {
transferredTicket(){
const myTicket = this.props.state.updatedTicket;
return myTicket
}
render() {
return (
<p>{this.transferredTicket()}</p>
);
}
}
When passing down a property from a parent to a child component, the property will be stored onto the props by the name it's passed through. For example:
class Parent extends Component {
state = {
ticket: '',
}
render() {
return <ChildComponent updatedTicket={this.state.ticket} />
}
}
class ChildComponent extends Component {
static propTypes = {
updatedTicket: PropTypes.string,
}
static defaultProps = {
updatedTicket: '',
}
render() {
return (
<div>{this.props.updatedTicket}</div>
);
}
}
In the example you've given, it doesn't seem like you're passing the state down to the component you're trying to access it in. In addition, it seems like you're trying to access the updatedTicket as a property of a state object, so just beware of how you're accessing your props.
Therefore, in order to access the updatedTicket property on the child component, you'll first need to import the PassTicket component, instantiate it in the parent (App) component, and pass the property down:
<PassTicket updateTicket={this.state.ticket} />
You would then be able to access the string in the PassTicket component like so - this.props.updateTicket
So .state in react is a local state that is only visible to the individual component. You can read more about it here: https://reactjs.org/docs/state-and-lifecycle.html
In order to pass your state around, you need to use the props system. So where you instantiate your component, you can pass in the state of the parent. For example:
<PassTicket ticket={this.state.updatedTicket}/>
Then inside your PassTicket render function, you can access the ticket prop:
render() {
const { ticket } = this.props
return (
<div>{ticket}</div>
)
}

React: parent component props in child without passing explicitly

Is it possible to have the props of the parent component to be available in child component without passing them down?
I am trying to implement a provider pattern, so that to access all the provider props in its child components.
EX:
Suppose the below provider comp FetchProvider will fetch the data and theme props on its own, and when any child component is enclosed by it, I want to access both props "data" and "theme" in the child component as well. How can we achieve it?
class FetchProvider
{
proptypes= {
data: PropTypes.shape({}),
theme: PropTypes.shape({})
}
render()
{
// do some
}
mapStateToProps()
{
return {data, theme};
}
}
class ChildComponent
{
proptypes= {
name: PropTypes.shape({})
}
render()
{
const{data, them} = this.props; // is this possible here?
// do some
}
}
and if I try to above components as below.
<FetchProvider>
<ChildComponent name="some value"/> //how can we access parent component props here? without passing them down
<FetchProvider/>
This is exactly what react context is all about.
A Consumer can access data the a Provider exposes no matter how deeply nested it is.
// Context lets us pass a value deep into the component tree
// without explicitly threading it through every component.
// Create a context for the current theme (with "light" as the default).
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
// Use a Provider to pass the current theme to the tree below.
// Any component can read it, no matter how deep it is.
// In this example, we're passing "dark" as the current value.
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
}
// A component in the middle doesn't have to
// pass the theme down explicitly anymore.
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton(props) {
// Use a Consumer to read the current theme context.
// React will find the closest theme Provider above and use its value.
// In this example, the current theme is "dark".
return (
<ThemeContext.Consumer>
{theme => <Button {...props} theme={theme} />}
</ThemeContext.Consumer>
);
}
Here is a small running example:
Note This is the react v16 context API.
Your use case can be solved with the usage of React context. With the help of Context, any child that is wrapped by a provided can be a consumer for the data that is provided by the Provider
In your case, you can use it like
context.js
export const FetchContext = React.createContext();
Provider.js
import { FetchContext } from 'path/to/context.js';
class FetchProvider extends React.Component
{
proptypes= {
data: PropTypes.shape({}),
theme: PropTypes.shape({})
}
render()
{
const { data, theme, children } = this.props;
return (
<FetchContext.Provider value={{ data, theme}}>
{children}
</FetchContext.Provider>
)
}
mapStateToProps()
{
return {data, theme};
}
}
ChildComponent.js
class ChildComponent extends React.Component
{
proptypes= {
name: PropTypes.shape({})
}
render()
{
const{data, them} = this.props; // use it from props here
// do some
}
}
export default (props) => (
<FetchContext.Consumer>
{({ data, theme }) => <ChildComponent {...props} data={data} theme={theme} />}
</FetchContext.Consumer>
)
However given the fact that you are already using Redux, which is build on the concept of Context, you might as well use redux and access the values within the child component since they are the same values that are supplied from the Redux store to the child by parent.
class ChildComponent extends React.Component
{
proptypes= {
name: PropTypes.shape({})
}
render()
{
const{data, them} = this.props; // use it from props here
// do some
}
}
const mapStateToProps = (state) => {
return {
data: state.data,
theme: state.theme
}
}
You can use React.Children to iterate over the children and pass whatever props you want to send to the new cloned elements using React.cloneElement.
EX:
class Parent extends React.Component {
constructor(props) {
super(props);
}
render() {
const { children } = this.props;
const newChildren = React.Children.map(children, child =>
React.cloneElement(child, { myProp: 'test' }));
return(
<View>
{newChildren}
</View>
)
}
}
Are you looking for:
class MyParent extends Component {
render() {
return <MyChild {...this.props}>
// child components
</MyChild>
}
}
This would pass all of the props passed into MyParent to the MyChild being rendered.

React js access to the state of another class

How can I access the state of another class.
This construction does not work
class classname2 extends React.Component {
...
this.state = { statename1: "lala" };
...
};
class classname1 extends React.Component {
render() {
return (
{classname2.state.statename1 }
);
}
};
As mentioned in the comments, pass state as props to their children.
class classname2 extends React.Component {
this.state = { statename1: "lala" };
render() {
return <classname1 statename1={this.state.statename1} />
}
};
class classname1 extends React.Component {
render() {
return (
<div>{this.props.statename1}</div>
);
}
};
An often used pattern is passing arbitrary props down the component tree:
const {needThisOne, andThisOne, ...props} = this.props;
// do stuff with needThisOne andThisOne
// and pass the remaining props down:
return <Component {...props} />;
An update for hooks, because why not.
const ParentComponent = ({...props}) => {
const [stateName1, setStateName1] = useState('defaultValue');
return <ChildComponent stateName1={stateName1} {...props} />;
}
const ChildComponent = ({stateName1, ...props}) => (
<span>{stateName1}</span>
);
Shared state between components by direct access is an anti-pattern. Each component should have its own state. If you need globally a available state, please consider using Redux.
It may sound a bit cumbersome at first but it's awesome and it allows your app to be properly tested.
Edit:
Passing state as props is also valid, but it only works when components are in parent-child order. Redux allows components to be updated no matter what their relationship is

Categories

Resources