how to pass props to a specific component in redux when iterating - reactjs

So I have this component in which I use the map function to call another component :
class configstepper extends React.Component {
...
//in render
if(this.props.selected_devices){
return(
this.props.selected_devices.map( (row, index) => (
<DeviceProps key={index} row={row} index={index} />
)))}
...}
Now my DeviceProps component is fetching data so it has a props to render
my problem is i want the props to be specific to a each DeviceProps not to all of them :
class deviceProperties extends Component {
...
//in render
<RaisedButton style={styles.Button}label="Read From Device" primary={true} onClick={this.handleRead.bind(this)}/>
<TextField id={this.props.index} value={this.props.read_data} readOnly/>
...
//handleRead where i trigger the action
handleRead(){
this.props.read_device()
}
//mapstate to props
function mapStateToProps(state){
return {
read_data:state.config.test}}
connect(mapStateToProps,actions)(deviceProperties)
Data is fetched on button click when i render all the DeviceProps components get the same data while i only want to get it on the clicked component.
I couldn't figure a work around this one

The mapStateToProps function accepts a second argument being the props you passed into the component from the parent, you could use these to the fetch a slice of your state that is specific to each of your DeviceProps instances. Equally the mapDispatchToProps also accepts the props as a second argument allowing you to create actions specific to each of your DeviceProps instances.
For example, say you were passing in a prop called deviceId into your DeviceProps component, with deviceId being a unique identifier for a specific device:
<DeviceProps key={index} row={row} index={index} deviceId={deviceId} />
Then you could use this unique identifier in your map state and props functions like so:
class DeviceProps extends Component {
render() {
...
}
}
function mapStateToProps(state, ownProps){
return {
device: state.devices[ownProps.deviceId]
}
}
function mapActionsToProps(dispatch, ownProps) {
return bindActionCreators({
readDeviceData: () => DeviceActions.readDeviceData(ownProps.deviceId)
}, dispatch)
}
connect(mapStateToProps, mapActionsToProps)(DeviceProps)

Related

Pass 'this' object to component - Typescript / React

I'd like to call a public method from the FirstComponent in my OtherComponent. However, I'm seeing this error:
"TypeError: firstCompObj.foo() is not a function" when I click the "Submit" button. After doing some debugging, the firstCompObj isn't showing up as type FirstComponent, but as an empty object.
I know that technically "foo" is a method, not a function, but what is the difference, and is it possible to call this method from the OtherComponent? Can I pass an object like this using the "this" keyword?
"FirstComponent.tsx":
export class FirstComponent: extends React.Component<{}, {}> {
..
public foo() {
// do something
}
private _otherComonent = (): JSX.Element => {
return (
<Other firstComponentObj={this} />
);
};
}
"otherfile.tsx":
export const Other: Component = (firstCompObj: FirstComponent) => {
...
<Button text='Submit' onClick={() => {firstCompObj.foo();}} />
...
}
You want to pass a function that calls foo() rather than passing the whole FirstComponent.
Other isn't actually a component right now because it doesn't take a props object. Lets make it accept the prop doFoo which is a function that requires no arguments and returns nothing.
interface OtherProps {
doFoo: () => void;
}
export const Other: React.FC<OtherProps> = ({ doFoo }) => {
return <button onClick={doFoo}>Submit</button>;
};
We we call Other in the render method of FirstComponent, we provide that doFoo prop. It will be an anonymous function that calls this.foo on the FirstComponent instance. The arrow => binds the this context.
export class FirstComponent extends React.Component<{}, {}> {
foo() {
// do something
console.log("called foo from FirstComponent");
}
render() {
return (
<div>
<p>There might be some content here</p>
<Other doFoo={() => this.foo()} />
</div>
);
}
}
FirstComponent could easily be a function component instead of a class component, but I'm keeping it the way that you had it.

Material UI's withTheme() hides component from ref prop

For the rare times when you need a reference to another JSX element in React, you can use the ref prop, like this:
class Widget extends React.PureComponent {
example() {
// do something
}
render() {
...
<Widget ref={r => this.mywidget = r}/>
<OtherWidget onClick={e => this.mywidget.example()}/>
Here, the Widget instance is stored in this.mywidget for later use, and the example() function can be called on it.
In Material UI, you can wrap components around a withTheme() call to make the theme accessible in their props:
export default withTheme()(Widget);
However if this is done, the ref receives an instance of WithTheme rather than Widget. This means the example() function is no longer accessible.
Is there some way to use ref with a component wrapped by withTheme() so that the underlying object can still be accessed, in the same manner as if withTheme() had not been used?
Here is an example demonstrating the issue. Lines 27 and 28 can be commented/uncommented to see that things only fail when the withTheme() call is added.
In order to get the ref of the component which is wrapped with withStyles, you can create a wrapper around Widget, and use that with withStyles like
const WithRefWidget = ({ innerRef, ...rest }) => {
console.log(innerRef);
return <Widget ref={innerRef} {...rest} />;
};
const MyWidget = withTheme()(WithRefWidget);
class Demo extends React.PureComponent {
constructor(props) {
super(props);
this.mywidget = null;
}
render() {
return (
<React.Fragment>
<MyWidget
innerRef={r => {
console.log(r);
this.mywidget = r;
}}
/>
<Button
onClick={e => {
e.preventDefault();
console.log(this.mywidget);
}}
variant="raised"
>
Click
</Button>
</React.Fragment>
);
}
}
Have a look at this answer to see an other alternative approach
losing functions when using recompose component as ref
This is a shorter alternative based on Shubham Khatri's answer. That answer works when you can't alter the inner component, this example is a bit shorter when you can modify the inner component.
Essentially ref doesn't get passed through withTheme() so you have to use a prop with a different name, and implement ref functionality on it yourself:
class Widget extends React.PureComponent {
constructor(props) {
props.ref2(this); // duplicate 'ref' functionality for the 'ref2' prop
...
const MyWidget = withTheme()(Widget);
...
<MyWidget
ref2={r => {
console.log(r);
this.mywidget = r;
}}
/>

Call a child component function from parent class in react.js [duplicate]

I have simple component called List which is a simple ul with some li inside. Each li is a simple component.
I have other parent component which render one input field and the List component. Tapping on Send key I catch text of input field. I want to call for example a function called handleNewText(inputText) but this function need to stay inside List component because the state I use to populate other li components live in List component.
I don' t want to refactor List and MyParent component passing the manage of data from List to MyParent.
first is parent and second is child
class TodoComp extends React.Component {
constructor(props){
super(props);
this.handleKeyPress = this.handleKeyPress.bind(this);
}
componentDidMpunt(){
console.log(this._child.someMethod());
}
handleKeyPress(event){
if(event.key === 'Enter'){
var t = event.target.value;
}
}
render(){
return (
<div>
<input
className="inputTodo"
type="text"
placeholder="want to be an hero...!"
onKeyPress={this.handleKeyPress}
/>
<List/>
</div>
);
}
}
export default class List extends React.Component {
constructor() {
super();
this.flipDone = this.flipDone.bind(this);
this.state = {
todos: Array(3).fill({ content: '', done: false})
};
}
flipDone(id) {
let index = Number(id);
this.setState({
todos: [
...this.state.todos.slice(0, index),
Object.assign({}, this.state.todos[index], {done: !this.state.todos[index].done}),
...this.state.todos.slice(index + 1)
]
});
}
render() {
const myList = this.state.todos.map((todo, index) => {
return (
<Todo key={index}
clickHandler={this.flipDone}
id={index}
todo={todo}
handleText={this.handleText}
/>
);
})
return (
<ul className="list">
{myList}
</ul>
);
}
ReactDOM.render(<TodoComp />,document.getElementById('myList'));
You need to make use of refs to call a function in the child component from the parent component
render the List component from parent as
<List ref="myList"/>
and then access the handleNewText() function as this.refs.myList.handleNewText()
UPDATE:
Strings refs are no longer recommended by React, you should rather use ref callbacks, check this
<List ref={(ref) => this.myList=ref}/>
and then access the child function like
this.myList.handleNewText()
Adding to #shubham-khatri solution:
If you are referencing a connected child component...
a. That child must say withRef: true in the (4th) config parameter:
#connect(store => ({
foo: store.whatever
…
}),null,null,{ withRef: true })
b. Access is through getWrappedInstance() (note, that getWrappedInstance also needs to be called ())
getWrappedInstance().howdyPartner()
I started learning React when functional component came out. Another way I experimented with some success is returning functions that you want to access as closures within a JSON. I like this method because closure is a construct of Javascript and it should still work even if React is updated yet again. Below is an example of child component
function Child(){
//declare your states and use effects
const [ppp, setPPP] = useState([]);
const [qqq, setQQQ] = useState(2);
//declare function that you want to access
function funcA(){ /*function to interact with your child components*/}
function funcB(){ /*function to interact with your child components*/}
//pure React functional components here
function Content(){
//function that you cannot access
funcC(){ /*.....*/}
funcD(){/*.......*/}
//what to render
return (
<div>
{/* your contents here */}
</div>
)
}
//return accessible contents and functions in a JSON
return {
content: Content, //function for rendering content
ExposeA: funcA, //return as a closure
ExposeB: funcB, //return as a closure
}
}
Below is an example of how you would render the child contents within the parent
function Parent(){
let chi = Child();
let ChildContent = chi.Content;
//calling your exposed functions
//these function can interacts with the states that affects child components
chi.ExposeA();
chi.ExposeB();
//render your child component
return (<div>
<div> {/* parent stuff here */</div>
<div> {/* parent stuff here */</div>
<ChildContent {/*Define your props here */} />
</div>)
}

Joining React props with custom props

When defining props in React, using Typescript, it seems that the default React props get overwritten by whatever the interface is. Is there a clean way to merge the two without having to specify every prop React already knows about?
Example:
interface IProps { // extends React.???
title: string;
// Generally seen adding children?: any
// But this can get out of hand with onClick, onChange, etc
}
function MyComponent(props: IProps) {
return props.children; // Typescript error: children doesn't exist on props
}
What you're referring to as "React default props" aka "every prop React already knows about" are more properly called "props accepted by any React DOM element wrapper component", i.e. onClick, className, etc.
Default props typically refers to the static defaultProps property on a React.Component, with which you provide default values for any props that were not passed to your component.
onClick, className, etc. are not reserved prop names and you can use them however you want in your own components, for instance you could have your component expect className to be a function (regardless of whether it's a good idea). The only reserved prop names that work on React elements of any kind (at the time of writing) are key and ref, and they're not really true props because they're not available to your component's render method.
Passing onClick to your own component does not automatically register a click handler. It will only do so if you pass the onClick you received to a <div>, <button>, or other React DOM Element wrapper that you render somewhere down the line. If you don't do anything with a prop you were passed, it has no effect (besides possibly causing a pure render component to update when it otherwise wouldn't).
For example, the following component will not respond to clicks:
const ClickFail = props => <button />
render(<ClickFail onClick={...} />, document.getElementById('root'))
But the following will:
const ClickSuccess = props => <button onClick={props.onClick} />
render(<ClickSuccess onClick={...} />, document.getElementById('root'))
And you could pass onClick to only one subelement if you really wanted:
const ClickButtonOnly = props => (
<form>
<input placeholder="ignores clicks" />
<button onClick={props.onClick}>Handles Clicks</button>
</form>
)
Or you could pass in multiple click handlers with different names:
const SimpleForm = props => (
<form>
<button onClick={props.onCancelClick}>Cancel</button>
<button onClick={props.onOKClick}>OK</button>
</form>
)
Also keep in mind that some DOM element wrappers accept props that others do not, for instance readOnly applies only to <input> and <textarea>.
You can even require children to be whatever type you want. For instance, you can pass a function as the children of a component and use it (again, not the best use of React, but just to illustrate what's possible):
type Props = {
value: number,
children: (value: number) => number,
}
const ApplyFunction = (props: Props) => (
<div>{React.Children.only(props.children)(props.value)}</div>
)
render(
<ApplyFunction value={3}>
{value => value * value}
</ApplyFunction>,
document.getElementById('root')
)
// renders <div>9</div>
So you see, IProps does not necessarily have to extend anything.
However, it is common to pass along rest props to a React DOM Element wrapper (e.g. <div {...props}>...</div> and as you were asking, it would be nice to be able to check the type of all of those input properties to your component.
I think you could do the following with Flow to check the types correctly, but unfortunately I don't think there's any Typescript equivalent (someone correct me if I'm wrong):
type Props = React.ElementProps<typeof 'div'> & {
title: string,
}
const MyComponent = (props: Props) => (
<div {...props}>
{props.title}
</div>
)
You should define that your stateless functional component will return React.SFC<YourProps>.
Try this
import * as React from "react";
const MyComponent: React.SFC<IProps> = (props) => {
return props.children;
}
If you want to use class-based component, you can extend your class with React.Component<YourProps(optional), YourState(optional)> instead
For example
import * as React from "react"
class MyComponent extends React.Component<IProps> {
public render(): JSX.Element {
return (
<div>...</div>
);
}
}
type TitleProps = { // your custom props
level?: level;
color?: string;
};
const Title = (props: TitleProps & React.Component['props']) => { // join default props and custom props
const { level = 'h1', color, children } = props; // joined props containing default props
return <Text style={[styles[level], color && { color }]}>{children}</Text>;
}
I was able to solve the problem by this way.

Call child component method from parent in react

I have simple component called List which is a simple ul with some li inside. Each li is a simple component.
I have other parent component which render one input field and the List component. Tapping on Send key I catch text of input field. I want to call for example a function called handleNewText(inputText) but this function need to stay inside List component because the state I use to populate other li components live in List component.
I don' t want to refactor List and MyParent component passing the manage of data from List to MyParent.
first is parent and second is child
class TodoComp extends React.Component {
constructor(props){
super(props);
this.handleKeyPress = this.handleKeyPress.bind(this);
}
componentDidMpunt(){
console.log(this._child.someMethod());
}
handleKeyPress(event){
if(event.key === 'Enter'){
var t = event.target.value;
}
}
render(){
return (
<div>
<input
className="inputTodo"
type="text"
placeholder="want to be an hero...!"
onKeyPress={this.handleKeyPress}
/>
<List/>
</div>
);
}
}
export default class List extends React.Component {
constructor() {
super();
this.flipDone = this.flipDone.bind(this);
this.state = {
todos: Array(3).fill({ content: '', done: false})
};
}
flipDone(id) {
let index = Number(id);
this.setState({
todos: [
...this.state.todos.slice(0, index),
Object.assign({}, this.state.todos[index], {done: !this.state.todos[index].done}),
...this.state.todos.slice(index + 1)
]
});
}
render() {
const myList = this.state.todos.map((todo, index) => {
return (
<Todo key={index}
clickHandler={this.flipDone}
id={index}
todo={todo}
handleText={this.handleText}
/>
);
})
return (
<ul className="list">
{myList}
</ul>
);
}
ReactDOM.render(<TodoComp />,document.getElementById('myList'));
You need to make use of refs to call a function in the child component from the parent component
render the List component from parent as
<List ref="myList"/>
and then access the handleNewText() function as this.refs.myList.handleNewText()
UPDATE:
Strings refs are no longer recommended by React, you should rather use ref callbacks, check this
<List ref={(ref) => this.myList=ref}/>
and then access the child function like
this.myList.handleNewText()
Adding to #shubham-khatri solution:
If you are referencing a connected child component...
a. That child must say withRef: true in the (4th) config parameter:
#connect(store => ({
foo: store.whatever
…
}),null,null,{ withRef: true })
b. Access is through getWrappedInstance() (note, that getWrappedInstance also needs to be called ())
getWrappedInstance().howdyPartner()
I started learning React when functional component came out. Another way I experimented with some success is returning functions that you want to access as closures within a JSON. I like this method because closure is a construct of Javascript and it should still work even if React is updated yet again. Below is an example of child component
function Child(){
//declare your states and use effects
const [ppp, setPPP] = useState([]);
const [qqq, setQQQ] = useState(2);
//declare function that you want to access
function funcA(){ /*function to interact with your child components*/}
function funcB(){ /*function to interact with your child components*/}
//pure React functional components here
function Content(){
//function that you cannot access
funcC(){ /*.....*/}
funcD(){/*.......*/}
//what to render
return (
<div>
{/* your contents here */}
</div>
)
}
//return accessible contents and functions in a JSON
return {
content: Content, //function for rendering content
ExposeA: funcA, //return as a closure
ExposeB: funcB, //return as a closure
}
}
Below is an example of how you would render the child contents within the parent
function Parent(){
let chi = Child();
let ChildContent = chi.Content;
//calling your exposed functions
//these function can interacts with the states that affects child components
chi.ExposeA();
chi.ExposeB();
//render your child component
return (<div>
<div> {/* parent stuff here */</div>
<div> {/* parent stuff here */</div>
<ChildContent {/*Define your props here */} />
</div>)
}

Resources