Reusing multiple instances of react component with different props
Asked Answered
C

3

6

So I have a child component that I want to render multiple instances of in a parent container component. Passing in different props to each so they display differently.

What is happening is that they are both being rendered with the last instance of the props in the script being read into both instances. Thus the both components below end up with placeHolder==='Describe yourself' Is there a work around for this so that they will each be injected with their props in turn exclusively?

           <ButtonMode 
              open={this.state.open}
              handleClose={this.handleClose}
              buttonName='Update'
              modalOpen={this.modalOpen}    
              placeHolder="New picture url"
              change={this.handlePicture}
              label='URL'
            />

           <ButtonMode 
              open={this.state.open}
              handleClose={this.handleClose}
              buttonName='Update'
              modalOpen={this.modalOpen}     
              placeHolder='Describe yourself'
              label='Bio'
              change={this.handleBio}
                />

ButtonMode

class ButtonMode extends Component {
    constructor(props){
        super(props)
        this.state = {
            input:''
        }
        this.handleInput = this.handleInput.bind(this);
        this.handle = this.handle.bind(this);
    }

    handleInput(val){
        this.setState({input:val})
    };

    handle() {

        this.props.change(this.state.input);
    };

    render(){
        const { classes } = this.props;
        return (
            <div>
                <Button 
                    className={classes.button}
                    onClick={this.props.modalOpen}
                    >Update
                </Button>
                <Modal
                    aria-labelledby="simple-modal-title"
                    aria-describedby="simple-modal-description"
                    open={this.props.open}
                    onClose={this.props.handleClose}
                    >
                    <div className={classes.paper}>
                        <TextField
                            id="filled-textarea"
                            label={this.props.label}
                            placeholder={this.props.placeHolder}
                            multiline
                            className={classes.textField}
                            onChange={(e)=>{this.handleInput(e.target.value)}}
                            rows= '4'
                            />
                        <Button 
                            onClick={this.handle}
                            className={classes.button} 
                            color="secondary">Submit</Button>                  
                  </div>
                </Modal>
            </div>
        )
    }
}

Then I used it like that

 class UserCard extends Component {
    constructor(props){
        super(props);
        this.state = {
          tempPro:'',
          open: false,
          profilePicture:''
        }
        this.modalOpen = this.modalOpen.bind(this);
        this.handleClose = this.handleClose.bind(this);
        this.handlePicture = this.handlePicture.bind(this);
      }



    // componentDidMount(){
    //   const {userId, profilePic} = this.props;
    //   this.setState({profilePicture:profilePic});
    //   // axios.get(`/api/profile/${userId}`).then(res=>{

    //   //   let {profilePic} = res.data[0];
    //   //   this.setState({profilePic})
    //   // })
    // }


    handlePicture(val){
      this.props.changePic(val);
      this.setState({open:false});
    };

    handleBio(val){

      this.setState({open:false});
    };

    handleClose(){
        this.setState({open: false});
    };
    modalOpen(){
      this.setState({open:true});
    };

    render() {
      const { classes } = this.props;
      const {stories} = this.props;
      let storyShow = stories.map((story,id) => {
        return(
          <div value={story.story_id}>
              <h3>{story.title}</h3>
              <ul className={classes.background}>
                <li>{story.description}</li>
                <li>{story.is_complete}</li>
              </ul>  
          </div>
        )
      });

      return (  
      <div className={classes.rootD}>
        <Grid container>
          <Grid className={classes.itemFix} >
          <Card className={classes.card}>
           <CardMedia
            className={classes.media}
            image={this.props.proPic}
            title={this.props.userName}
            />
            <div>
            <ButtonMode 
                  open={this.state.open}
                  handleClose={this.handleClose}
                  modalOpen={this.modalOpen}    
                  placeHolder="New picture url"
                  change={this.handlePicture}
                  label='URL'
                />
            </div>       
          <CardHeader
            className={classes.titles}
            title={this.props.userName}
            subheader="Somewhere"
            />
            <CardHeader className={classes.titles} title='Bio' />
              <CardContent className={classes.background}>
                <Typography className={classes.bio} paragraph>
                  {this.props.bio}
                </Typography>
              </CardContent> 
              <div>
                <ButtonMode 
                  open={this.state.open}
                  handleClose={this.handleClose}
                  modalOpen={this.modalOpen}     
                  placeHolder='Describe you how you want'
                  label='Bio'
                  change={this.handleBio}
                    />
              </div>
          </Card>
          </Grid>
          <Grid className={classes.itemFixT}>
            <Card className={classes.card}>
            <CardContent>
                <CardHeader 
                  className={classes.titles}
                  title='Works'/>
                <Typography paragraph>
                  <ul>
                    {storyShow}
                  </ul>
                </Typography>
              </CardContent>
            </Card>
          </Grid>
          </Grid>
      </div>
      );
    }
  }

  UserCard.propTypes = {
    classes: PropTypes.object.isRequired,
  };
  function mapStateToProps(state){
    const {userId, profilePic} = state;
    return {
      userId,
      profilePic      
    }
  }

  export default connect(mapStateToProps,{})(withStyles(styles)(UserCard));
Chromium answered 29/12, 2018 at 20:59 Comment(5)
React actually works the way allowing you to have multiple instances of the same component having different props. So it should be something wrong in their code. maybe they are wrapped with buggy HOC or for some reason props comes into static properties. add component's code.Allimportant
Can you post the entire component these are being rendered in as well as the ButtonMode component?Pirandello
@Pirandello just did. Thanks for the fresh eyes.Chromium
@Allimportant i am using Material UI. But i have done similar to this before with no issues.Chromium
nothing looks wrong so far. have you tried just to put ButtonMode directly omitting UserCard? I believe you should get it worked properly. This way you can start with debugging.Allimportant
A
5

I had a similar issue where I was trying to pass different functions to the children components. I had a UploadFile component that contained an <input/> and a <Button/> from material-ui, and I wanted to reuse this component multiple times throughout a page, as the user has multiple files to upload, and in order to save the files, I needed callback functions in the main page.

What I had to do, was give each child component <UploadFile/> in my case, and <ButtonMode/> in your case, a unique id passed in as a prop, since otherwise, the top level page cannot tell each reference to the child component apart from any others.

The code of the child component:

function UploadFile({ value, handleFile }) {
const classes = useStyles();

return (
<>
  <input
    accept=".tsv,.fa,.fasta"
    className={classes.input}
    id={value}
    type="file"
    style={{ display: 'none' }}
    onChange={e => handleFile(e.target.files[0])}
  />
  <label htmlFor={value}>
    <Button
      variant="contained"
      color='default'
      component="span"
      startIcon={<CloudUploadIcon />}
      className={classes.button}>
      Upload
    </Button>
  </label>
</>
);
}

The usage of this component in the parent (handleFile is the function I am passing in and is defined above in the parent component):

<UploadFile value='prosite' handleFile={handlePrositeFile} />
<UploadFile value='pfam' handleFile={handlePfamFile} />
Apace answered 27/10, 2020 at 20:15 Comment(1)
exactly my case, super helpful, thank youGustaf
F
3

I spent an embarrassingly long time on a similar issue. I tried all sorts of JS debugging and even re-read the entire concept of closure :)

This is was my culprit: <TextField id="filled-textarea" ... />

i.e. the id is static. If we have multiple instances of the same id on one page, we have a problem.

Make id dynamic, e.g. <TextField id={this.props.label} ... />

Finished answered 31/1, 2021 at 4:47 Comment(0)
C
0

I was using the same state for both modals and in each instance of handleOpen() it was only ever opening the last instance of modal in the script.

Chromium answered 31/12, 2018 at 2:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.