locked
What is my mistake in using Context in React? RRS feed

  • Question

  • User887623398 posted

    Hello,

    You could see below the process of creating, providing, and consuming context. Which step is wrong that my code doesn't work?

    imageContext.js

    import React from 'react';
    const ImageContext = React.CreateContext('null');
    export default ImageContext;

    ImageUpload.js

    return (
                <ImageContext.provider value={this.state.selectedFile.name}>
                <div>
                    <div style={mystyle} onClick={this.onNavToEditor}/>
                    <h1>File Upload Demo</h1>
                    <div >{this.state.uploadResult} onClick={this.onNavToEditor}</div>
                    <div>
                        <input type="file" onChange={this.onFileChange} />
                        <input type="text" value={this.state.value} onChange={this.onDescriptionChange} />
                        <button onClick={this.onFileUpload}>
                            Upload!
                        </button>
                     
                    </div>
    
                    {this.listItems()}
                   
                    </div>
                </ImageContext.provider>

    ImageEditor.js

     return (
            <ImageContext.Consumer>
                {context => (
            <div className="home-page">
                <div className="center">
                    <h1>Photo Editor</h1>
                    <Button className='button' onClick={saveImageToDisk}>Save Image to Disk</Button>
                    </div>
                    
                        <ImageEditor
                            includeUI={{
                                loadImage: {
                                    path: context.path,
                                    name: context,
                                },
                                theme: myTheme,
                                menu: ["crop", "flip", "rotate", "draw", "shape", "text", "filter"],
                                initMenu: "",
                                uiSize: {
                                    height: `calc(100vh - 160px)`,
                                },
                                menuBarPosition: "bottom",
                            }}
                            cssMaxHeight={window.innerHeight}
                            cssMaxWidth={window.innerWidth}
                            selectionStyle={{
                                cornerSize: 20,
                                rotatingPointOffset: 70,
                            }}
                            usageStatistics={true}
                            ref={imageEditor}
                        />
                   
                    </div>
                )}
            </ImageContext.Consumer>
        );

    thanks,

    Saeed

    Monday, February 22, 2021 2:30 PM

All replies

  • User-474980206 posted

    you don't say what is not working. nor do you show any code that sets a value to the context. but odd stuff in your sample code:

    setting the default context to the string 'null'. why? everywhere you use it you expect it to be an object

    when you set the context value via the provider you use:

       this.state.selectedFile.name

    which again appears to be a string.

    then in the consumer (assuming its a child of the provider, you don't explain how its rendered), you expect the context to an object:

                     loadImage: {
                                    path: context.path,
                                    name: context,
                                },

    as you do not  show how ImageUpload.js state is set or updated, we don't know what value is passed to the ImageContext.Provider prop.

    Monday, February 22, 2021 4:24 PM
  • User887623398 posted

    I mean after clicking the image thumbnail, it isn't being opened in the image editor:

    ImageUpload.js

    
    import React, { Component } from 'react';
    import { Redirect } from "react-router-dom";
    import ImageContext from '../ImageContext';
    
    export default class ImageUpload extends Component {
        static displayName = ImageUpload.name;
    
        constructor(props) {
            super(props);
            this.state = {
                redirect: null,
                selectedFile: null,
                description: null,
                uploadResult: null,
                fileIdList: []
    
            };
    
            this.onNavToEditor = this.onNavToEditor.bind(this);
        }
    
        getList = () => {
            fetch('api/Image')
                .then(response => response.json())
                .then(data => this.setState({ fileIdList: data }));
        };
    
        componentDidMount() {
            this.getList();
        };
    
        onFileChange = event => {
            this.setState({ selectedFile: event.target.files[0] });
        };
        onDescriptionChange = event => {
            this.setState({ description: event.target.value })
        };
        onFileUpload = async () => {
    
            const formData = new FormData();
    
            formData.append(
                "ImageData.File",
                this.state.selectedFile,
                this.state.selectedFile.name
            );
    
    
            fetch('/api/Image', {
                method: 'POST',
                body: formData
            }).then(resposne => resposne.json())
                .then(data => {
                    console.log(data);
                    this.setState({ uploadResult: "File " + data.fileName + " successfully uploaded." });
                    this.getList();
                });
           
        };
    
    
        onNavToEditor = async () => {
    
            this.setState({ redirect: "/ImageEditor" });
    
    
        };
    
    
    
        listItems = () => {
            const listItems = this.state.fileIdList.map((item) =>
                <div key={item.imageId.toString()}>
                    <img src={"/api/Image/DownloadImage/" + item.imageId}
                        alt={item.fileName}
                        className="img-thumbnail"
                        height="100" width="100" />
                </div>
            );
            return (<div>{listItems}</div>);
        };
    
        render() {
            if (this.state.redirect) {
                return <Redirect to={this.state.redirect} />
            };
            const mystyle = {
    
                backgroundColor: "DodgerBlue",
                margin: "100px",
    
                height: "100px",
                width: "100px"
    
    
            };
    
            return (
                <ImageContext.Provider value={this.state.selectedFile.name}>
                    <div>
                        <div style={mystyle} onClick={this.onNavToEditor} />
                        <h1>File Upload Demo</h1>
                        <div >{this.state.uploadResult} onClick={this.onNavToEditor}</div>
                        <div>
                            <input type="file" onChange={this.onFileChange} />
                            <input type="text" value={this.state.value} onChange={this.onDescriptionChange} />
                            <button onClick={this.onFileUpload}>
                                Upload!
                        </button>
    
                        </div>
    
                        {this.listItems()}
    
                    </div>
    
                    </ImageContext.Provider >
    
            );
    
        }
    }

    ImageEditor:

    import React, { useState, useEffect } from "react";
    import "./ImageEditor.css";
    import "tui-image-editor/dist/tui-image-editor.css";
    import ImageEditor from "@toast-ui/react-image-editor";
    import Button from "react-bootstrap/Button";
    import ImageContext from "../ImageContext";
    
    const icona = require("tui-image-editor/dist/svg/icon-a.svg");
    const iconb = require("tui-image-editor/dist/svg/icon-b.svg");
    const iconc = require("tui-image-editor/dist/svg/icon-c.svg");
    const icond = require("tui-image-editor/dist/svg/icon-d.svg");
    const download = require("downloadjs");
    const myTheme = {
        "menu.backgroundColor": "white",
        "common.backgroundColor": "#151515",
        "downloadButton.backgroundColor": "white",
        "downloadButton.borderColor": "white",
        "downloadButton.color": "black",
        "menu.normalIcon.path": icond,
        "menu.activeIcon.path": iconb,
        "menu.disabledIcon.path": icona,
        "menu.hoverIcon.path": iconc,
    };
    
    export default function App() {
    
    
        const [imageSrc, setImageSrc] = useState("");
        const imageEditor = React.createRef();
    
        //using the context
        const currentImage = React.useContext(ImageContext);
    
    
    
        const saveImageToDisk = () => {
            
            const imageEditorInst = imageEditor.current.imageEditorInst;
            const data = imageEditorInst.toDataURL();
    
    
    
            if (data) {
                const mimeType = data.split(";")[0];
                const extension = data.split(";")[0].split("/")[1];
                download(data, `image.${extension}`, mimeType);
            }
                
    
        };
    
        return (
            <ImageContext.Consumer>
                {context => (
            <div className="home-page">
                <div className="center">
                    <h1>Photo Editor</h1>
                    <Button className='button' onClick={saveImageToDisk}>Save Image to Disk</Button>
                    </div>
                    
                        <ImageEditor
                            includeUI={{
                                loadImage: {
                                    path: context.path,
                                    name: context,
                                },
                                theme: myTheme,
                                menu: ["crop", "flip", "rotate", "draw", "shape", "text", "filter"],
                                initMenu: "",
                                uiSize: {
                                    height: `calc(100vh - 160px)`,
                                },
                                menuBarPosition: "bottom",
                            }}
                            cssMaxHeight={window.innerHeight}
                            cssMaxWidth={window.innerWidth}
                            selectionStyle={{
                                cornerSize: 20,
                                rotatingPointOffset: 70,
                            }}
                            usageStatistics={true}
                            ref={imageEditor}
                        />
                   
                    </div>
                )}
            </ImageContext.Consumer>
        );
       
    }

    ImageEditor.js and ImageUpload.js both of them are components.

    Monday, February 22, 2021 8:08 PM
  • User-474980206 posted

    You have a consumer at the top of the tree. This is not how it works. The provider goes to the top. And all you are putting in the context is the file name, not the properties you are trying to use.

    The structure of you app is very confusing. Normally app.js would only have routing and common page chrome (layout). 

    Tuesday, February 23, 2021 3:37 AM
  • User887623398 posted

    I changed the provider:

     onFileChangeName = event => {
            this.setState({ selectedFileName: event.target.files[0].name });
        };

    Now:

     return (
                <ImageContext.Provider value={this.onFileChangeName}>
                
                </ImageContext.Provider >

    And consumer:

    return (
            <ImageContext.Consumer>
                {value => (
            <div className="home-page">
                <div className="center">
                    <h1>Photo Editor</h1>
                    <Button className='button' onClick={saveImageToDisk}>Save Image to Disk</Button>
                    </div>
                
                        <ImageEditor
                            includeUI={{
    
                                loadImage: {
                                    path: value.path,
                                    name: value,
                                },
                                theme: myTheme,
                                menu: ["crop", "flip", "rotate", "draw", "shape", "text", "filter"],
                                initMenu: "",
                                uiSize: {
                                    height: `calc(100vh - 160px)`,
                                },
                                menuBarPosition: "bottom",
                            }}
                            cssMaxHeight={window.innerHeight}
                            cssMaxWidth={window.innerWidth}
                            selectionStyle={{
                                cornerSize: 20,
                                rotatingPointOffset: 70,
                            }}
                            usageStatistics={true}
                            ref={imageEditor}
                        />
                   
                    </div>
                )}
            </ImageContext.Consumer>
        );
       
    }

    But when I run the app, I encounter this error: TypeError: Cannot read property 'path' of undefined

    Also react dev tools highlights the: path: value.path. as the mistake.

    Do you think why?

    Thursday, February 25, 2021 1:50 PM
  • User-474980206 posted

    In the provider you set value to a function. So when you use value in the consumer it’s still a function. Normally you would call it in the consumer somewhere. as it a state setter you’d expect it to be used as an event handler. Not sure why you expect the function to have properties.

    You should go thru some tutorials. You appear to be just trying random code samples.

    Thursday, February 25, 2021 3:57 PM
  • User887623398 posted

    I understood what you say but I read tutorials!

    Thursday, February 25, 2021 4:30 PM
  • User-474980206 posted

    You need to actually code the tutorial and use the debugger to understand the flow, and why each line of code is there. You should tweak the code code in the tutorial to understand how changes are made.  Once you understand, you can google for examples.

    Monday, March 1, 2021 4:57 PM