How to use refs with react-native-router-flux - reactjs

For 2 simple components, parent, child both using Redux I'm able to use refs, to invoke a child method. But how can I achieve same using above library 'react-native-router-flux', where can I set ref to Scenes.
ParentComponent
var Parent = new React.createClass({
render : function() {
return (<Child ref={(child) => {this.child = child}})
}
// able to trigger child.somefunction via this.child.getWrappedInstance().somefunction()
})
module.exports = connect(select)(Parent)
ChildComponent
var Child = new React.createClass({
somefunction: function(){
//working
}
})
module.exports = connect(select, null, null, {withRef: true})(Child)
https://github.com/aksonov/react-native-router-flux
render function of Child component after using above library
render: function() {
return (
<Router>
<Scene>
<Scene key="WelcomeModal" component={WelcomeModal} title="welcomeTitle" initial={true}
navigationBarStyle={styles.welcomeModal_navigationBarStyle}
titleStyle={styles.welcomeModal_titleStyle}
rightTitle='INFO'
rightButtonStyle={styles.baseModal_rightButtonStyle}
rightButtonTextStyle={styles.baseModal_rightButtonTextStyle}
rightButtonIconStyle={styles.baseModal_rightButtonIconStyle}
ref={(_WelcomeModal) => {this._WelcomeModal = _WelcomeModal}} //need some way to add ref here
/>
</Scene>
</Router>
)
}
https://github.com/aksonov/react-native-router-flux

Related

Linking React components using props.childern [duplicate]

I'm trying to find the proper way to define some components which could be used in a generic way:
<Parent>
<Child value="1">
<Child value="2">
</Parent>
There is a logic going on for rendering between parent and children components of course, you can imagine <select> and <option> as an example of this logic.
This is a dummy implementation for the purpose of the question:
var Parent = React.createClass({
doSomething: function(value) {
},
render: function() {
return (<div>{this.props.children}</div>);
}
});
var Child = React.createClass({
onClick: function() {
this.props.doSomething(this.props.value); // doSomething is undefined
},
render: function() {
return (<div onClick={this.onClick}></div>);
}
});
The question is whenever you use {this.props.children} to define a wrapper component, how do you pass down some property to all its children?
Cloning children with new props
You can use React.Children to iterate over the children, and then clone each element with new props (shallow merged) using React.cloneElement.
See the code comment why I don't recommend this approach.
const Child = ({ childName, sayHello }) => (
<button onClick={() => sayHello(childName)}>{childName}</button>
);
function Parent({ children }) {
// We pass this `sayHello` function into the child elements.
function sayHello(childName) {
console.log(`Hello from ${childName} the child`);
}
const childrenWithProps = React.Children.map(children, child => {
// Checking isValidElement is the safe way and avoids a
// typescript error too.
if (React.isValidElement(child)) {
return React.cloneElement(child, { sayHello });
}
return child;
});
return <div>{childrenWithProps}</div>
}
function App() {
// This approach is less type-safe and Typescript friendly since it
// looks like you're trying to render `Child` without `sayHello`.
// It's also confusing to readers of this code.
return (
<Parent>
<Child childName="Billy" />
<Child childName="Bob" />
</Parent>
);
}
ReactDOM.render(<App />, document.getElementById("container"));
<script src="https://unpkg.com/react#17/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom#17/umd/react-dom.production.min.js"></script>
<div id="container"></div>
Calling children as a function
Alternatively, you can pass props to children via render props. In this approach, the children (which can be children or any other prop name) is a function which can accept any arguments you want to pass and returns the actual children:
const Child = ({ childName, sayHello }) => (
<button onClick={() => sayHello(childName)}>{childName}</button>
);
function Parent({ children }) {
function sayHello(childName) {
console.log(`Hello from ${childName} the child`);
}
// `children` of this component must be a function
// which returns the actual children. We can pass
// it args to then pass into them as props (in this
// case we pass `sayHello`).
return <div>{children(sayHello)}</div>
}
function App() {
// sayHello is the arg we passed in Parent, which
// we now pass through to Child.
return (
<Parent>
{(sayHello) => (
<React.Fragment>
<Child childName="Billy" sayHello={sayHello} />
<Child childName="Bob" sayHello={sayHello} />
</React.Fragment>
)}
</Parent>
);
}
ReactDOM.render(<App />, document.getElementById("container"));
<script src="https://unpkg.com/react#17/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom#17/umd/react-dom.production.min.js"></script>
<div id="container"></div>
For a slightly cleaner way to do it, try:
<div>
{React.cloneElement(this.props.children, { loggedIn: this.state.loggedIn })}
</div>
Edit:
To use with multiple individual children (the child must itself be a component) you can do. Tested in 16.8.6
<div>
{React.cloneElement(this.props.children[0], { loggedIn: true, testPropB: true })}
{React.cloneElement(this.props.children[1], { loggedIn: true, testPropA: false })}
</div>
Try this
<div>{React.cloneElement(this.props.children, {...this.props})}</div>
It worked for me using react-15.1.
Use {...this.props} is suggested in https://reactjs.org/docs/jsx-in-depth.html#spread-attributes
Pass props to direct children.
See all other answers
Pass shared, global data through the component tree via context
Context is designed to share data that can be considered “global” for a tree of React components, such as the current authenticated user, theme, or preferred language. 1
Disclaimer: This is an updated answer, the previous one used the old context API
It is based on Consumer / Provide principle. First, create your context
const { Provider, Consumer } = React.createContext(defaultValue);
Then use via
<Provider value={/* some value */}>
{children} /* potential consumers */
</Provider>
and
<Consumer>
{value => /* render something based on the context value */}
</Consumer>
All Consumers that are descendants of a Provider will re-render whenever the Provider’s value prop changes. The propagation from Provider to its descendant Consumers is not subject to the shouldComponentUpdate method, so the Consumer is updated even when an ancestor component bails out of the update. 1
Full example, semi-pseudo code.
import React from 'react';
const { Provider, Consumer } = React.createContext({ color: 'white' });
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: { color: 'black' },
};
}
render() {
return (
<Provider value={this.state.value}>
<Toolbar />
</Provider>
);
}
}
class Toolbar extends React.Component {
render() {
return (
<div>
<p> Consumer can be arbitrary levels deep </p>
<Consumer>
{value => <p> The toolbar will be in color {value.color} </p>}
</Consumer>
</div>
);
}
}
1 https://facebook.github.io/react/docs/context.html
Passing Props to Nested Children
With the update to React Hooks you can now use React.createContext and useContext.
import * as React from 'react';
// React.createContext accepts a defaultValue as the first param
const MyContext = React.createContext();
functional Parent(props) {
const doSomething = React.useCallback((value) => {
// Do something here with value
}, []);
return (
<MyContext.Provider value={{ doSomething }}>
{props.children}
</MyContext.Provider>
);
}
function Child(props: { value: number }) {
const myContext = React.useContext(MyContext);
const onClick = React.useCallback(() => {
myContext.doSomething(props.value);
}, [props.value, myContext.doSomething]);
return (
<div onClick={onClick}>{props.value}</div>
);
}
// Example of using Parent and Child
import * as React from 'react';
function SomeComponent() {
return (
<Parent>
<Child value={1} />
<Child value={2} />
</Parent>
);
}
React.createContext shines where React.cloneElement case couldn't handle nested components
function SomeComponent() {
return (
<Parent>
<Child value={1} />
<SomeOtherComp>
<Child value={2} />
</SomeOtherComp>
</Parent>
);
}
The best way, which allows you to make property transfer is children like a function pattern
https://medium.com/merrickchristensen/function-as-child-components-5f3920a9ace9
Code snippet: https://stackblitz.com/edit/react-fcmubc
Example:
const Parent = ({ children }) => {
const somePropsHere = {
style: {
color: "red"
}
// any other props here...
}
return children(somePropsHere)
}
const ChildComponent = props => <h1 {...props}>Hello world!</h1>
const App = () => {
return (
<Parent>
{props => (
<ChildComponent {...props}>
Bla-bla-bla
</ChildComponent>
)}
</Parent>
)
}
You can use React.cloneElement, it's better to know how it works before you start using it in your application. It's introduced in React v0.13, read on for more information, so something along with this work for you:
<div>{React.cloneElement(this.props.children, {...this.props})}</div>
So bring the lines from React documentation for you to understand how it's all working and how you can make use of them:
In React v0.13 RC2 we will introduce a new API, similar to
React.addons.cloneWithProps, with this signature:
React.cloneElement(element, props, ...children);
Unlike cloneWithProps, this new function does not have any magic
built-in behavior for merging style and className for the same reason
we don't have that feature from transferPropsTo. Nobody is sure what
exactly the complete list of magic things are, which makes it
difficult to reason about the code and difficult to reuse when style
has a different signature (e.g. in the upcoming React Native).
React.cloneElement is almost equivalent to:
<element.type {...element.props} {...props}>{children}</element.type>
However, unlike JSX and cloneWithProps, it also preserves refs. This
means that if you get a child with a ref on it, you won't accidentally
steal it from your ancestor. You will get the same ref attached to
your new element.
One common pattern is to map over your children and add a new prop.
There were many issues reported about cloneWithProps losing the ref,
making it harder to reason about your code. Now following the same
pattern with cloneElement will work as expected. For example:
var newChildren = React.Children.map(this.props.children, function(child) {
return React.cloneElement(child, { foo: true })
});
Note: React.cloneElement(child, { ref: 'newRef' }) DOES override the
ref so it is still not possible for two parents to have a ref to the
same child, unless you use callback-refs.
This was a critical feature to get into React 0.13 since props are now
immutable. The upgrade path is often to clone the element, but by
doing so you might lose the ref. Therefore, we needed a nicer upgrade
path here. As we were upgrading callsites at Facebook we realized that
we needed this method. We got the same feedback from the community.
Therefore we decided to make another RC before the final release to
make sure we get this in.
We plan to eventually deprecate React.addons.cloneWithProps. We're not
doing it yet, but this is a good opportunity to start thinking about
your own uses and consider using React.cloneElement instead. We'll be
sure to ship a release with deprecation notices before we actually
remove it so no immediate action is necessary.
more here...
I needed to fix accepted answer above to make it work using that instead of this pointer. This within the scope of map function didn't have doSomething function defined.
var Parent = React.createClass({
doSomething: function() {
console.log('doSomething!');
},
render: function() {
var that = this;
var childrenWithProps = React.Children.map(this.props.children, function(child) {
return React.cloneElement(child, { doSomething: that.doSomething });
});
return <div>{childrenWithProps}</div>
}})
Update: this fix is for ECMAScript 5, in ES6 there is no need in var that=this
Method 1 - clone children
const Parent = (props) => {
const attributeToAddOrReplace= "Some Value"
const childrenWithAdjustedProps = React.Children.map(props.children, child =>
React.cloneElement(child, { attributeToAddOrReplace})
);
return <div>{childrenWithAdjustedProps }</div>
}
Full Demo
Method 2 - use composable context
Context allows you to pass a prop to a deep child component without explicitly passing it as a prop through the components in between.
Context comes with drawbacks:
Data doesn't flow in the regular way - via props.
Using context creates a contract between the consumer and the provider. It might be more difficult to understand and replicate the requirements needed to reuse a component.
Using a composable context
export const Context = createContext<any>(null);
export const ComposableContext = ({ children, ...otherProps }:{children:ReactNode, [x:string]:any}) => {
const context = useContext(Context)
return(
<Context.Provider {...context} value={{...context, ...otherProps}}>{children}</Context.Provider>
);
}
function App() {
return (
<Provider1>
<Provider2>
<Displayer />
</Provider2>
</Provider1>
);
}
const Provider1 =({children}:{children:ReactNode}) => (
<ComposableContext greeting="Hello">{children}</ComposableContext>
)
const Provider2 =({children}:{children:ReactNode}) => (
<ComposableContext name="world">{children}</ComposableContext>
)
const Displayer = () => {
const context = useContext(Context);
return <div>{context.greeting}, {context.name}</div>;
};
None of the answers address the issue of having children that are NOT React components, such as text strings. A workaround could be something like this:
// Render method of Parent component
render(){
let props = {
setAlert : () => {alert("It works")}
};
let childrenWithProps = React.Children.map( this.props.children, function(child) {
if (React.isValidElement(child)){
return React.cloneElement(child, props);
}
return child;
});
return <div>{childrenWithProps}</div>
}
Cleaner way considering one or more children
<div>
{ React.Children.map(this.props.children, child => React.cloneElement(child, {...this.props}))}
</div>
If you have multiple children you want to pass props to, you can do it this way, using the React.Children.map:
render() {
let updatedChildren = React.Children.map(this.props.children,
(child) => {
return React.cloneElement(child, { newProp: newProp });
});
return (
<div>
{ updatedChildren }
</div>
);
}
If your component is having just one child, there's no need for mapping, you can just cloneElement straight away:
render() {
return (
<div>
{
React.cloneElement(this.props.children, {
newProp: newProp
})
}
</div>
);
}
Parent.jsx:
import React from 'react';
const doSomething = value => {};
const Parent = props => (
<div>
{
!props || !props.children
? <div>Loading... (required at least one child)</div>
: !props.children.length
? <props.children.type {...props.children.props} doSomething={doSomething} {...props}>{props.children}</props.children.type>
: props.children.map((child, key) =>
React.cloneElement(child, {...props, key, doSomething}))
}
</div>
);
Child.jsx:
import React from 'react';
/* but better import doSomething right here,
or use some flux store (for example redux library) */
export default ({ doSomething, value }) => (
<div onClick={() => doSomething(value)}/>
);
and main.jsx:
import React from 'react';
import { render } from 'react-dom';
import Parent from './Parent';
import Child from './Child';
render(
<Parent>
<Child/>
<Child value='1'/>
<Child value='2'/>
</Parent>,
document.getElementById('...')
);
see example here: https://plnkr.co/edit/jJHQECrKRrtKlKYRpIWl?p=preview
Got inspired by all the answers above and this is what I have done. I am passing some props like some data, and some components.
import React from "react";
const Parent = ({ children }) => {
const { setCheckoutData } = actions.shop;
const { Input, FieldError } = libraries.theme.components.forms;
const onSubmit = (data) => {
setCheckoutData(data);
};
const childrenWithProps = React.Children.map(
children,
(child) =>
React.cloneElement(child, {
Input: Input,
FieldError: FieldError,
onSubmit: onSubmit,
})
);
return <>{childrenWithProps}</>;
};
Here's my version that works with single, multiple, and invalid children.
const addPropsToChildren = (children, props) => {
const addPropsToChild = (child, props) => {
if (React.isValidElement(child)) {
return React.cloneElement(child, props);
} else {
console.log("Invalid element: ", child);
return child;
}
};
if (Array.isArray(children)) {
return children.map((child, ix) =>
addPropsToChild(child, { key: ix, ...props })
);
} else {
return addPropsToChild(children, props);
}
};
Usage example:
https://codesandbox.io/s/loving-mcclintock-59emq?file=/src/ChildVsChildren.jsx:0-1069
Further to #and_rest answer, this is how I clone the children and add a class.
<div className="parent">
{React.Children.map(this.props.children, child => React.cloneElement(child, {className:'child'}))}
</div>
Maybe you can also find useful this feature, though many people have considered this as an anti-pattern it still can be used if you're know what you're doing and design your solution well.
Function as Child Components
I think a render prop is the appropriate way to handle this scenario
You let the Parent provide the necessary props used in child component, by refactoring the Parent code to look to something like this:
const Parent = ({children}) => {
const doSomething(value) => {}
return children({ doSomething })
}
Then in the child Component you can access the function provided by the parent this way:
class Child extends React {
onClick() => { this.props.doSomething }
render() {
return (<div onClick={this.onClick}></div>);
}
}
Now the fianl stucture will look like this:
<Parent>
{(doSomething) =>
(<Fragment>
<Child value="1" doSomething={doSomething}>
<Child value="2" doSomething={doSomething}>
<Fragment />
)}
</Parent>
The slickest way to do this:
{React.cloneElement(this.props.children, this.props)}
According to the documentation of cloneElement()
React.cloneElement(
element,
[props],
[...children]
)
Clone and return a new React element using element as the starting
point. The resulting element will have the original element’s props
with the new props merged in shallowly. New children will replace
existing children. key and ref from the original element will be
preserved.
React.cloneElement() is almost equivalent to:
<element.type {...element.props} {...props}>{children}</element.type>
However, it also preserves refs. This means that if you get a child
with a ref on it, you won’t accidentally steal it from your ancestor.
You will get the same ref attached to your new element.
So cloneElement is what you would use to provide custom props to the children. However there can be multiple children in the component and you would need to loop over it. What other answers suggest is for you to map over them using React.Children.map. However React.Children.map unlike React.cloneElement changes the keys of the Element appending and extra .$ as the prefix. Check this question for more details: React.cloneElement inside React.Children.map is causing element keys to change
If you wish to avoid it, you should instead go for the forEach function like
render() {
const newElements = [];
React.Children.forEach(this.props.children,
child => newElements.push(
React.cloneElement(
child,
{...this.props, ...customProps}
)
)
)
return (
<div>{newElements}</div>
)
}
You no longer need {this.props.children}. Now you can wrap your child component using render in Route and pass your props as usual:
<BrowserRouter>
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/posts">Posts</Link></li>
<li><Link to="/about">About</Link></li>
</ul>
<hr/>
<Route path="/" exact component={Home} />
<Route path="/posts" render={() => (
<Posts
value1={1}
value2={2}
data={this.state.data}
/>
)} />
<Route path="/about" component={About} />
</div>
</BrowserRouter>
For any one who has a single child element this should do it.
{React.isValidElement(this.props.children)
? React.cloneElement(this.props.children, {
...prop_you_want_to_pass
})
: null}
When using functional components, you will often get the TypeError: Cannot add property myNewProp, object is not extensible error when trying to set new properties on props.children. There is a work around to this by cloning the props and then cloning the child itself with the new props.
const MyParentComponent = (props) => {
return (
<div className='whatever'>
{props.children.map((child) => {
const newProps = { ...child.props }
// set new props here on newProps
newProps.myNewProp = 'something'
const preparedChild = { ...child, props: newProps }
return preparedChild
})}
</div>
)
}
Is this what you required?
var Parent = React.createClass({
doSomething: function(value) {
}
render: function() {
return <div>
<Child doSome={this.doSomething} />
</div>
}
})
var Child = React.createClass({
onClick:function() {
this.props.doSome(value); // doSomething is undefined
},
render: function() {
return <div onClick={this.onClick}></div>
}
})
I came to this post while researching for a similar need, but i felt cloning solution that is so popular, to be too raw and takes my focus away from the functionality.
I found an article in react documents Higher Order Components
Here is my sample:
import React from 'react';
const withForm = (ViewComponent) => {
return (props) => {
const myParam = "Custom param";
return (
<>
<div style={{border:"2px solid black", margin:"10px"}}>
<div>this is poc form</div>
<div>
<ViewComponent myParam={myParam} {...props}></ViewComponent>
</div>
</div>
</>
)
}
}
export default withForm;
const pocQuickView = (props) => {
return (
<div style={{border:"1px solid grey"}}>
<div>this is poc quick view and it is meant to show when mouse hovers over a link</div>
</div>
)
}
export default withForm(pocQuickView);
For me i found a flexible solution in implementing the pattern of Higher Order Components.
Of course it depends on the functionality, but it is good if someone else is looking for a similar requirement, it is much better than being dependent on raw level react code like cloning.
Other pattern that i actively use is the container pattern. do read about it, there are many articles out there.
In case anyone is wondering how to do this properly in TypeScript where there are one or multiple child nodes. I am using the uuid library to generate unique key attributes for the child elements which, of course, you don't need if you're only cloning one element.
export type TParentGroup = {
value?: string;
children: React.ReactElement[] | React.ReactElement;
};
export const Parent = ({
value = '',
children,
}: TParentGroup): React.ReactElement => (
<div className={styles.ParentGroup}>
{Array.isArray(children)
? children.map((child) =>
React.cloneElement(child, { key: uuidv4(), value })
)
: React.cloneElement(children, { value })}
</div>
);
As you can see, this solution takes care of rendering an array of or a single ReactElement, and even allows you to pass properties down to the child component(s) as needed.
Some reason React.children was not working for me. This is what worked for me.
I wanted to just add a class to the child. similar to changing a prop
var newChildren = this.props.children.map((child) => {
const className = "MenuTooltip-item " + child.props.className;
return React.cloneElement(child, { className });
});
return <div>{newChildren}</div>;
The trick here is the React.cloneElement. You can pass any prop in a similar manner
Render props is most accurate approach to this problem. Instead of passing the child component to parent component as children props, let parent render child component manually. Render is built-in props in react, which takes function parameter. In this function you can let parent component render whatever you want with custom parameters. Basically it does the same thing as child props but it is more customizable.
class Child extends React.Component {
render() {
return <div className="Child">
Child
<p onClick={this.props.doSomething}>Click me</p>
{this.props.a}
</div>;
}
}
class Parent extends React.Component {
doSomething(){
alert("Parent talks");
}
render() {
return <div className="Parent">
Parent
{this.props.render({
anythingToPassChildren:1,
doSomething: this.doSomething})}
</div>;
}
}
class Application extends React.Component {
render() {
return <div>
<Parent render={
props => <Child {...props} />
}/>
</div>;
}
}
Example at codepen
There are lot of ways to do this.
You can pass children as props in parent.
example 1 :
function Parent({ChildElement}){
return <ChildElement propName={propValue} />
}
return <Parent ChildElement={ChildComponent}/>
Pass children as Function
example 2 :
function Parent({children}){
return children({className: "my_div"})
}
OR
function Parent({children}){
let Child = children
return <Child className='my_div' />
}
function Child(props){
return <div {...props}></div>
}
export <Parent>{props => <Child {...props} />}</Parent>
I did struggle to have the listed answers work but failed. Eventually, I found out that the issue is with correctly setting up the parent-child relationship. Merely nesting components inside other components does not mean that there is a parent-child relationship.
Example 1. Parent-child relationship;
function Wrapper() {
return (
<div>
<OuterComponent>
<InnerComponent />
</OuterComponent>
</div>
);
}
function OuterComponent(props) {
return props.children;
}
function InnerComponent() {
return <div>Hi! I'm in inner component!</div>;
}
export default Wrapper;
Example 2. Nested components:
function Wrapper() {
return (
<div>
<OuterComponent />
</div>
);
}
function OuterComponent(props) {
return <InnerComponent />
}
function InnerComponent() {
return <div>Hi! I'm in inner component!</div>;
}
export default Wrapper;
As I said above, props passing works in Example 1 case.
The article below explains it https://medium.com/#justynazet/passing-props-to-props-children-using-react-cloneelement-and-render-props-pattern-896da70b24f6

reactjs edit child state from parent

I have an app with 3 components.
The first is App.jsx
which calls the TodoList component as follows:
<TodoList items={this.state.items} loaded={this.state.loaded} />
the TodoList component renders multiple TodoListItem components
module.exports = React.createClass({
render: function(){
return (
<ul>
{this.renderList()}
</ul>
)
},
renderList: function(){
var children = [];
for(var key in this.props.items) {
if(this.props.items[key].text){
var listItem = this.props.items[key];
listItem.key = key;
children.push(
<TodoListItem item={listItem} key={key} onEdit={this.handleItemEdit} />
)
}
}
return children;
},
handleItemEdit: function(component){
console.log(component);
}
});
Then in my TodolistItem component im rendering multiple li elements
module.exports = React.createClass({
getInitialState: function(){
return {
text: this.props.item.text,
done: this.props.item.done
}
},
render: function(){
return (
<li onClick="this.props.onEdit.bind(null,this)">{this.state.text}</li>
)
},
});
When i click on the li the function handlItemEdit on the parent element function is fired, my question is how i can change the text value of the child element in it's parent's handleItemEdit function?
What im trying to do is when you click on a li open a bootstrap modal with an input field, change its text,save and pass the new props to TodoListItem
In TodoList component you should write getInitialState and save items from props to state, then in render pass items from state (not from props like you did) to TodoListItem component:
for(var key in this.state.items) {
if(this.state.items[key].text){
var listItem = this.state.items[key];
listItem.key = key;
children.push(
<TodoListItem item={listItem} key={key} onEdit={this.handleItemEdit} />
)
}
}
If you call setState within handleItemEdit method, render() will see the updated state and will be executed.
In this case all you need to do is change state in your handleItemEdit and this will be passed to TodolistItem where you should use props, not state in render, like this:
render: function(){
return (
<li onClick="this.props.onEdit.bind(null,this.props)">{this.props.text}</li>
)
},
Notice you are passing props onEdit, so you have to chane handleItemEdit like this:
handleItemEdit: function(itemProps){
console.log('You clicked: ' + this.props.items[itemProps.key]);
}
I have tested solution of your problem in ECMAScript 6 syntax. It would be very easy to use this solution in your notation too. Basicaly what you have to do is to pass this from parent to child, and then use it when you bind onClick event to parent (so you can have this in event handler function) as secound parameter you pass props of the child, where key prop will point the very cilcked element. Then it is very easy to chane parent's state, this will trigger render on parent, and cascade to render child with the new (changed by user) props.
TodoList.js
class TodoList extends Component {
render (){
return (
<ul>
{this.props.items.map(function(listItem, key) {
return (listItem.text &&
<TodoListItem item={listItem} todoList={this} key={key} onEdit={this.handleItemEdit}/>
);
})}
</ul>
)
};
handleItemEdit(itemProps){
console.log('You clicked: ' + this.props.items[itemProps.key].text);
};
}
export default TodoList;
TodoListItem.js
import React, { Component } from 'react';
class TodoListItem extends Component {
render() {
return (
<li onClick={this.props.onEdit.bind(this.props.todoList, this.props.item)}>{this.props.item.text}</li>
)
}
}
export default TodoListItem;
I have tested it and it prints the very text you have clicked in console. This means that oyu have access to this TodoListItem and you can change state of it in TodoList component.

ReactJs - is it possible to set the url or querystring based on state of component tree

I have a component that has several nested components, specifically tabs, pills, etc. No all are visible at any one time.
I want to be able to set their default state/props based on the URL parameters (or URL) but I'm not clear how to achieve this in React.
For example, I have a component with two tabs (bootstrap). When the user selects the second tab, can i modify the URL with a parameter so that is the page is refreshed, the state of the currently selected tab is retained.
I've included the Router but cannot find any simple examples of what I'm trying to achieve.
What I'd like to be able to do is access/parse the query string parameters and set the state and ultimately properties of the child components.
Any advice is really welcome.
I can provide example of the tab component, but don't think it would add any value to the question.
The way I've gone about this it to take advantage of React contexts. It's an undocumented but powerful feature that kind of acts like mutable props that can be accessed by a component and all its children.
I suggest Googling about it. It's a bit tough to wrap your mind around, but starts to make sense once you play with it. Here is an example excerpt from some code I am working on right now:
var React = require('react');
var Router = require('react-router'),
Route = Router.Route,
RouteHandler = Router.RouteHandler;
var ReactBootstrap = require('react-bootstrap'),
DropdownButton = ReactBootstrap.DropdownButton,
Nav = ReactBootstrap.Nav,
Navbar = ReactBootstrap.Navbar;
var ReactRouterBootstrap = require('react-router-bootstrap'),
MenuItemLink = ReactRouterBootstrap.MenuItemLink;
var Nav = React.createClass({
render: function() {
return (
<Navbar brand='My Cool Page!' inverse toggleNavKey={0}>
<Nav right eventKey={0}> {}
<DropdownButton eventKey={2} title='Projects'>
<MenuItemLink eventKey="1" to="myCoolPage" params={{ awesomeName: "matt" }}>Go to My Cool Page!</MenuItemLink>
</DropdownButton>
</Nav>
</Navbar>
);
}
);
var MyCoolPage = React.createClass({
contextTypes: {
router: React.PropTypes.func
},
getInitialState: function() {
return {
awesomeName: ''
};
},
componentWillMount: function() {
// Called once before the first render
this.getAwesomeName();
},
componentWillReceiveProps: function(nextProps) {
// Called when props change after the first render
// From my testing, this is also called when a context changes
this.getAwesomeName();
},
getAwesomeName: function() {
var { router } = this.context;
var awesomeName = router.getCurrentParams().awesomeName;
this.setState({awesomeName: awesomeName});
},
render: function() {
return (
<h1>My awesome name is: {this.state.awesomeName}</h1>
);
}
});
var App = React.createClass({
render: function() {
return (
<div>
<Nav />
<RouteHandler {...props} />
</div>
);
}
});
var routes = (
<Route handler={App}>
<Route name="myCoolPage" path="myCoolPage/:awesomeName" handler={MyCoolPage} />
</Route>
);
Router.run(routes, Router.HashLocation, function (Root) {
React.render(<Root/>, document.body);
});

Call a React component method from outside

I want to call a method exposed by a React component from the instance of a React Element.
For example, in this jsfiddle. I want to call the alertMessage method from the HelloElement reference.
Is there a way to achieve this without having to write additional wrappers?
Edit (copied code from JSFiddle)
<div id="container"></div>
<button onclick="onButtonClick()">Click me!</button>
var onButtonClick = function () {
//call alertMessage method from the reference of a React Element! Something like HelloElement.alertMessage()
console.log("clicked!");
}
var Hello = React.createClass({displayName: 'Hello',
alertMessage: function() {
alert(this.props.name);
},
render: function() {
return React.createElement("div", null, "Hello ", this.props.name);
}
});
var HelloElement = React.createElement(Hello, {name: "World"});
React.render(
HelloElement,
document.getElementById('container')
);
There are two ways to access an inner function. One, instance-level, like you want, another, static level.
Instance
You need to call the function on the return from React.render. See below.
Static
Take a look at ReactJS Statics. Note, however, that a static function cannot access instance-level data, so this would be undefined.
var onButtonClick = function () {
//call alertMessage method from the reference of a React Element!
HelloRendered.alertMessage();
//call static alertMessage method from the reference of a React Class!
Hello.alertMessage();
console.log("clicked!");
}
var Hello = React.createClass({
displayName: 'Hello',
statics: {
alertMessage: function () {
alert('static message');
}
},
alertMessage: function () {
alert(this.props.name);
},
render: function () {
return React.createElement("div", null, "Hello ", this.props.name);
}
});
var HelloElement = React.createElement(Hello, {
name: "World"
});
var HelloRendered = React.render(HelloElement, document.getElementById('container'));
Then do HelloRendered.alertMessage().
You can do like
import React from 'react';
class Header extends React.Component{
constructor(){
super();
window.helloComponent = this;
}
alertMessage(){
console.log("Called from outside");
}
render(){
return (
<AppBar style={{background:'#000'}}>
Hello
</AppBar>
)
}
}
export default Header;
Now from outside of this component you can called like this below
window.helloComponent.alertMessage();
1. With React hooks - useImperativeHandle + useRef
const MyComponent = ({myRef}) => {
const handleClick = () => alert('hello world')
useImperativeHandle(myRef, () => ({
handleClick
}), [/* dependencies (if any) */])
return (<button onClick={handleClick}>Original Button</button>)
}
MyComponent.defaultProps = {
myRef: {current: {}}
}
const MyParentComponent = () => {
const myRef = React.useRef({})
return (
<>
<MyComponent
myRef={myRef}
/>
<button onClick={myRef.current.handleClick}>
Additional Button
</button>
</>
)
}
2. With only React hook - useRef
const MyComponent = ({myRef}) => {
const handleClick = () => alert('hello world')
myRef.current.handleClick = handleClick
return (<button onClick={handleClick}>Original Button</button>)
}
MyComponent.defaultProps = {
myRef: {current: {}}
}
const MyParentComponent = () => {
const myRef = React.useRef({})
return (
<>
<MyComponent
myRef={myRef}
/>
<button onClick={myRef.current.handleClick}>
Additional Button
</button>
</>
)
}
Good Luck...
I've done something like this:
class Cow extends React.Component {
constructor (props) {
super(props);
this.state = {text: 'hello'};
}
componentDidMount () {
if (this.props.onMounted) {
this.props.onMounted({
say: text => this.say(text)
});
}
}
render () {
return (
<pre>
___________________
< {this.state.text} >
-------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
</pre>
);
}
say (text) {
this.setState({text: text});
}
}
And then somewhere else:
class Pasture extends React.Component {
render () {
return (
<div>
<Cow onMounted={callbacks => this.cowMounted(callbacks)} />
<button onClick={() => this.changeCow()} />
</div>
);
}
cowMounted (callbacks) {
this.cowCallbacks = callbacks;
}
changeCow () {
this.cowCallbacks.say('moo');
}
}
I haven't tested this exact code, but this is along the lines of what I did in a project of mine and it works nicely :). Of course this is a bad example, you should just use props for this, but in my case the sub-component did an API call which I wanted to keep inside that component. In such a case this is a nice solution.
With the render method potentially deprecating the returned value, the recommended approach is now to attach a callback ref to the root element. Like this:
ReactDOM.render( <Hello name="World" ref={(element) => {window.helloComponent = element}}/>, document.getElementById('container'));
which we can then access using window.helloComponent, and any of its methods can be accessed with window.helloComponent.METHOD.
Here's a full example:
var onButtonClick = function() {
window.helloComponent.alertMessage();
}
class Hello extends React.Component {
alertMessage() {
alert(this.props.name);
}
render() {
return React.createElement("div", null, "Hello ", this.props.name);
}
};
ReactDOM.render( <Hello name="World" ref={(element) => {window.helloComponent = element}}/>, document.getElementById('container'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container"></div>
<button onclick="onButtonClick()">Click me!</button>
You can just add an onClick handler to the div with the function (onClick is React's own implementation of onClick) and you can access the property within { } curly braces, and your alert message will appear.
In case you wish to define static methods that can be called on the component class - you should use statics. Although:
"Methods defined within this block are static, meaning that you can run them before any component instances are created, and the methods do not have access to the props or state of your components. If you want to check the value of props in a static method, have the caller pass in the props as an argument to the static method." (source)
Some example code:
const Hello = React.createClass({
/*
The statics object allows you to define static methods that can be called on the component class. For example:
*/
statics: {
customMethod: function(foo) {
return foo === 'bar';
}
},
alertMessage: function() {
alert(this.props.name);
},
render: function () {
return (
<div onClick={this.alertMessage}>
Hello {this.props.name}
</div>
);
}
});
React.render(<Hello name={'aworld'} />, document.body);
Hope this helps you a bit, because i don't know if I understood your question correctly, so correct me if i interpreted it wrong:)
It appears statics are deprecated, and the other methods of exposing some functions with render seem convoluted. Meanwhile, this Stack Overflow answer about debugging React, while seeming hack-y, did the job for me.
If you are in ES6 just use the "static" keyword on your method from your example would be the following: static alertMessage: function() {
...
},
Hope can help anyone out there :)
I use this helper method to render components and return an component instance.
Methods can be called on that instance.
static async renderComponentAt(componentClass, props, parentElementId){
let componentId = props.id;
if(!componentId){
throw Error('Component has no id property. Please include id:"...xyz..." to component properties.');
}
let parentElement = document.getElementById(parentElementId);
return await new Promise((resolve, reject) => {
props.ref = (component)=>{
resolve(component);
};
let element = React.createElement(componentClass, props, null);
ReactDOM.render(element, parentElement);
});
}
class AppProvider extends Component {
constructor() {
super();
window.alertMessage = this.alertMessage.bind(this);
}
alertMessage() {
console.log('Hello World');
}
}
You can call this method from the window by using window.alertMessage().
method 1 using ChildRef:
public childRef: any = React.createRef<Hello>();
public onButtonClick= () => {
console.log(this.childRef.current); // this will have your child reference
}
<Hello ref = { this.childRef }/>
<button onclick="onButtonClick()">Click me!</button>
Method 2: using window register
public onButtonClick= () => {
console.log(window.yourRef); // this will have your child reference
}
<Hello ref = { (ref) => {window.yourRef = ref} }/>`
<button onclick="onButtonClick()">Click me!</button>
With React17 you can use useImperativeHandle hook.
useImperativeHandle customizes the instance value that is exposed to parent components when using ref. As always, imperative code using refs should be avoided in most cases. useImperativeHandle should be used with forwardRef:
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);
In this example, a parent component that renders would be able to call inputRef.current.focus().
Though this question is closed, I would like to share another approach.
Here's what worked for me:
Child Component
Child component accepts a prop, let's call it onExportedMethods, the aim is to return the set of instance methods that this component wants to give to consumers.
The decision of what needs to be exposed is done at constructor level.
Consumer Component
pass method for prop onExportedMethods & in the handler keep copy of the set of methods Child component exposes.
Whenever required, parent component can call the exposed method
Checkout the sample here
For dynamic components I used the getDerivedStateFromProps method with props.
You can create function that update the props of the child component, The getDerivedStateFromProps in the child component will handle the update of the props for you.
For example:
class Parent extends React.Component
{
constructor(props)
{
super(props);
this.state = { selectMachine: '1' };
this.setComponent = null;
}
handleMachineChange = (e) =>{
this.setState({selectMachine: e.target.value})
}
}
class Child extends React.Component
{
state = {
programForm: {
machine_id: '1',
}
}
constructor(props)
{
super(props);
}
static getDerivedStateFromProps(props, state) {
if(props.selectMachine !== state.programForm.machine_id){
//Change in props
return{
programForm: { ...state.programForm, machine_id: props.selectMachine }
};
}
return null; // No change to state
}
}

Access component context inside ReactRouter's willTransitionTo and willTransitionFrom

I have a requirement to fetch data from a server everytime a page is rendered.
Usually this (ajax) request should be performed inside componentDidMount, but with ReactRouter, componentDidMount is not fired when a url has changed.
This is my attempt so far:
// App.js
var App = React.createClass({
render: function() {
return (
<RouteHandler />
);
}
var routes = (
<Route handler={App} path="/" >
...
<Route handler={PageA} name="PageA" path="/pagea" />
...
</Route>
);
Router.run(routes, Router.HashLocation, function(Handler) {
React.render(<Handler/>, document.body);
});
});
// PageA.js
var PageA = React.createClass({
statics: {
willTransitionTo: function (transition, params, query, callback) {
console.log("willTransitionTo PageA");
this.handleGet(callback);
}
, willTransitionFrom: function (transition, component) {
console.log("willTransitionFrom PageA");
}
}
, handleGet(callback) { ... }
...
}
I would like to intercept an event 'page will appear' and willTransitionTo (and willTransitionFrom) seems to do the job as expected. However, the problem is I can't access PageA.handleGet inside that method. I would appreciate if you can guide me to a right path for achieving this task as I might misunderstand some important concepts of using a router.
P.S. I didn't use Flux (yet).
I found a workaround at here where fetched data is passed down, as props, to a target component (PageA) by using a wrapper. Instead of managing ajax requests in the target component's componentDidMount, I have to do it in the wrapper component's componentDidMount. This way, App.js can become very large if there are many pages dealing with pre-loading requests.
// App.js
var PageAWrapper = React.createClass({
getInitialState: function() {
return {
data: []
};
}
, handleGet: function() {
var promise = $.ajax({...});
promise.done(function(data) {
this.setState({data: data});
}.bind(this));
}
, componentDidMount: function() {
this.handleGet();
}
, render: function () {
return (
<PageA data={this.state.data} />
);
}
});
...
var routes = (
<Route handler={App} path="/" >
...
<Route handler={PageAWrapper} name="PageA" path="/pagea" />
...
</Route>
);
EDIT
Because data is mutable inside PageA, but I am forced to pass it as props. Therefore, I also have to modify PageA slightly.
var PageA = React.createClass({
getInitialState: function() {
return {
data: []
};
}
, componentWillReceiveProps: function(nextProps) {
this.setState({data: nextProps.data})
}
, render: function() {
... use this.state.data to render ...
}
});

Resources