React Context error on rendering - javascript

I created an app to learn ReactJS. Unfortunately, when I was trying to use context I got 1 error on rendering, but my app compiles well.
This is my code:
import React, {Component} from 'react';
const LoginContext = React.createContext(null);
const user = {
isLoggedIn: true,
username: 'test',
};
class App extends Component {
constructor(props) {
super(props);
this.state = {
isLoggedIn: false,
user: user,
};
}
render() {
return (
<LoginContext.Provider user={this.state.user}>
<Welcome/>
</LoginContext.Provider>
);
}
}
class Welcome extends Component {
render() {
return (
<div>
<WelcomeText/>
</div>
);
}
}
class WelcomeText extends Component {
render() {
return (
<LoginContext.Consumer>
<div>
{(user) => (<p>{user.username}</p>)}
</div>
</LoginContext.Consumer>
);
}
}
export default App;
This is the error:
updateContextConsumer
http://localhost:3000/static/js/bundle.js:20927:23
20924 | {
20925 | ReactCurrentOwner.current = workInProgress;
20926 | ReactDebugCurrentFiber.setCurrentPhase('render');
> 20927 | newChildren = render(newValue);
| ^ 20928 | ReactDebugCurrentFiber.setCurrentPhase(null);
20929 | } // React DevTools reads this flag.
20930 |
Can you help me solve this?

ContextProvider needs a prop called value and not user
<LoginContext.Provider value={this.state.user}>
<Welcome/>
</LoginContext.Provider>
Also the Context consumer has the children as a function and not the div
class WelcomeText extends Component {
render() {
return (
<LoginContext.Consumer>
{(user) => (<div><p>{user.username}</p></div>)}
</LoginContext.Consumer>
);
}
}

I am currently working on React-16.8.6 and having an interesting bug.
I'm not sure if it's the known issue or not but I am having the same error whenever I have a space between two characters '}' and '<' as you can see it line-30.
After (1) removing the space or (2) completely making a new line with , it was resolved.
Even though I love React a lot, it's not perfect and we can make it better together.

Related

OnClick is not working, but no errors appear in the console

As I am currently learning React, I am creating a joke generator. I created a local file and was able to pull the data (jokes) and display it on the browser. Now, I am trying to create a button that when you click, it displays a random joke. I can see the joke and button, but no action is being triggered. I check the console and there are no errors. If I use onClick = {randomJoke}, then I get an error saying listener was expecting a function, not an object. Can someone point help me out and point out what is wrong?
This is how I currently have it set up:
import React from 'react'
import SportsJokesData from './SportsJokesData';
class SportsJokesApi extends React.Component {
constructor(props){
super(props);
this.getRandomJoke = this.getRandomJoke.bind(this);
}
getRandomJoke(){
return SportsJokesData[(SportsJokesData.length * Math.random()) << 0]
}
render() {
const randomJoke = this.getRandomJoke()
return (
<React.Fragment>
<p> {randomJoke.question}</p>
<p>{randomJoke.answer}</p>
<button onClick={this.getRandomJoke}>
click here
</button>
</React.Fragment>
)
}
}
export default SportsJokesApi;
This is how the file was originally scripted prior to adding the button.
import React from 'react'
import SportsJokesData from './SportsJokesData';
class SportsJokesApi extends React.Component {
getRandomJoke(){
return SportsJokesData[(SportsJokesData.length * Math.random()) << 0]
}
render() {
const randomJoke = this.getRandomJoke()
return (
<React.Fragment>
<p>{randomJoke.question}</p>
<h1>{randomJoke.answer}</h1>
</React.Fragment>
)
}
}
export default SportsJokesApi;
You cannot just return a new component on onClick event. You need to set some state first. This guide is useful: https://flaviocopes.com/react-show-different-component-on-click/
You need to re-render your React component, when there is a new joke. To do this store the randomJoke in the state of your React Component.
When you call this.getRandomJoke you should update the state along with it, so that a re-render of the React component is triggered. When this happens the UI will be updated with the latest value of the randomJoke
import React from 'react'
import SportsJokesData from './SportsJokesData';
const initialState = {
randomJoke: null
};
class SportsJokesApi extends React.Component {
constructor(props) {
super(props);
this.getRandomJoke = this.getRandomJoke.bind(this);
this.state = initialState;
}
getRandomJoke() {
this.setState({
randomJoke: SportsJokesData[(SportsJokesData.length * Math.random()) << 0]
});
}
render() {
const { randomJoke } = this.state;
return (
<React.Fragment >
<p> {randomJoke.question} </p>
<p> {randomJoke.answer} </p>
<button onClick = {this.getRandomJoke}>click here </button>
</React.Fragment >
);
}
}
export default SportsJokesApi;

How to Programmatically Provide and Consume Context?

So my question is a simple one. In React js I want to pass some states and handlers from a parent to its 3rd grandchild using Context. I have implemented this within the jsx but I want to use the states within the javascript o that I have some logic before I completely output my states.
I have divided my question into 2 parts. 1.) What I have done so far. 2.) What I want to do essentially.
1.)
// this file just stores the Context
MyContext.js
import React, { Component } from 'react';
export const MyContext = React.createContext();
MyProvider.js // this class is used by the parent and the child to have access to the provider
import React, { Component } from 'react';
import {MyContext} from '../MyContext'
class MyProvider extends Component {
state = {
name: 'Wes',
age: 100,
cool: true
}
render() {
return (
<MyContext.Provider value={{
state: this.state,
growAYearOlder: () => this.setState({
age: this.state.age + 1
})
}}>
{this.props.children}
</MyContext.Provider>
)
}
}
export default MyProvider;
// Ok so now I am basically skipping the parent and showing you the consumer grand-child
Person.js
import React, { Component } from 'react';
// first we will make a new context
import { MyContext } from '../MyContext';
class Person extends Component {
render() {
return (
<div className="person">
<MyContext.Consumer>
{(context) => (
<React.Fragment>
<p>Age: {context.state.age}</p>
<p>Name: {context.state.name}</p>
<button onClick={context.growAYearOlder}>🍰🍥🎂</button>
</React.Fragment>
)}
</MyContext.Consumer>
</div>
)
}
}
export default Person;
2.)
// Ok so as you can see here I have had to immediately use the context.growAYearOlder. What I want to do instead is have control of it using javascript and modify it as desired; So something like this:
Child.js
const parentContext = MyContext.getContext();
if(somethingHappens){
parentContext().growAYearOlder();
}
return(
// The now rendered component
);
I tried something like this but it doesnt work:
MyContext.Consumer.context.growAYearOlder();
There are many similar questions with proper answers, docs, examples and so on - but this question kept popping up for me.
So, in case you want to get the context value and use it within your component's render() just import it (export context itself not only provider) and use _currentValue e.g.
const contextData = MyContext._currentValue;
Note that you still have to wrap your components with your given context provider.
Also note that for function components, you need to use useContext e.g.
const contextData = useContext(MyContext);
And for class components you can assign the context to a static var and then use it e.g.
class Main extends React.Component(){
static contextType = MyContext;
componentDidMount(){
const contextData = this.context;
}
render() {
return (
<p>Hey</p>
);
}
Note that the static var has to be called contextType otherwise this.context won't hold the MyContext data.
I've based my answer solely from the docs itself(https://reactjs.org/docs/context.html#updating-context-from-a-nested-component)
import React, { Component } from 'react';
import { MyContext } from '../MyContext'
class MyProvider extends Component {
constructor(props) {
super(props)
// I've moved the state declaration inside the constructor
this.state = {
name: 'Wes',
age: 100,
cool: true
}
// moved the function here and added prevState
this.growAYearOlder = () => {
this.setState(prevState => ({
age: prevState.age + 1,
}))
};
}
render() {
return (
<MyContext.Provider value={{
state: this.state,
growAYearOlder: this.growAYearOlder,
}}>
{this.props.children}
</MyContext.Provider>
)
}
}
export default MyProvider;

How to properly render Component after this.setState in React

I have this React component
import React, { Component } from "react";
export default class ResourceForField extends Component {
constructor() {
super();
this.state = {
resources: [],
};
}
componentDidMount() {
// get the resources from the Link props and save it into the state
this.setState({
resources: this.props.location.resources,
});
}
// This component gets the id of current learningField from the url
// and the rest(like the resources) from the Link component
render() {
return (
<div>
{this.state.resources.map(res => (
<div>test</div>
))}
</div>
);
}
}
It gets the resources from the Link component, and that works fine. If I check out the state of the Component from the dev tools, the state looks right. And I thought with my logic this should work. So firstly, the state is empty, the component gets rendered, since the state is empty it doesn't render any components. Then, setState gets called, it gets all the resources and saves them into the state, and then the component would re-render, and it should work, but it doesn't. I'm getting a TypeError: Cannot read property 'map' of undefined error. What is the correct way to do this and how do I fix this?
Try this code:
import React, { Component } from "react";
export default class ResourceForField extends Component {
constructor() {
super();
this.state = {
resources: this.props && this.props.location && this.props.location.resources?this.props.location.resources:[],
};
}
componentDidMount() {
}
// This component gets the id of current learningField from the url
// and the rest(like the resources) from the Link component
render() {
return (
<div>
{this.state.resources.map(res => (
<div>test</div>
))}
</div>
);
}
}
Or use directly props
import React, { Component } from "react";
export default class ResourceForField extends Component {
constructor() {
super();
}
// This component gets the id of current learningField from the url
// and the rest(like the resources) from the Link component
render() {
return (
<div>
{
this.props && this.props.location &&
this.props.location.resources
?this.props.location.resources.map(res => (
<div>test</div>
))
:null
}
</div>
);
}
}
Or use componentWillReceiveProps or getDerivedStateFromProps life cycle methods.
Check this.props.location.resources is array.
See more: https://hackernoon.com/replacing-componentwillreceiveprops-with-getderivedstatefromprops-c3956f7ce607
For first check is this.props.location.resources array, or if data type changes you can add checking, you can use lodash isArray or with js like this:
import React, { Component } from "react";
export default class ResourceForField extends Component {
constructor() {
super();
this.state = {
resources: [],
};
}
componentDidMount() {
// get the resources from the Link props and save it into the state
Array.isArray(this.props.location.resources) {
this.setState({
resources: this.props.location.resources,
});
}
}
// This component gets the id of current learningField from the url
// and the rest(like the resources) from the Link component
render() {
return (
<div>
{this.state.resources.map(res => (
<div>test</div>
))}
</div>
);
}
}
Or you can just use hooks like this:
import React, { useState, useEffect } from "react";
export default function ResourceForField({location}) {
const [ resources, setResources ] = useState([]);
useEffect(() => {
if (location && Array.isArray(location.resources)) {
setResources(location.resources)
}
}, [location]);
return (
<div>
{resources.map(res => (
<div>test</div>
))}
</div>
);
}
If the internal state of ResourceForField doesn't change and always equals to its prop, you shouldn't save the prop in the state. You can instead create a pure functional component.
Also note that there's nothing preventing you from initializing the state from the props in constructor method. i.e. you're not required to wait for the component to mount in order to access the props.
So, I'd write the following component for ResourceForField:
function ResourceForField({resources = []}) {
return (
<div>
{
resources.map(res => (<div>test</div>))
}
</div>
);
}

Unexpected token error in React app

Ok so I have an issue where I've been following along with a Udacity course.
The problem is the entire course app has been contained in one file and has steadily become harder and harder to pore through to find where issues are.
At some point an error has crept in, I think it's a syntax error, where I've added or missed out a bracket or something along those lines, but I can't find where it's happened.
I think it's specific to the below App component but can't see what I have done wrong.
The syntax highlighting in VS Code points to the below line where const is not highlighted as it is in other areas of the code.
const Context = React.createContext()
class App extends React.Component {
const Context = React.createContext()
class ConnectedApp extends React.Component {
render() {
return (
<Context.Consumer>
{(store) => (
<App store={store}/>
)}
</Context.Consumer>
)
}
}
class Provider extends React.Component {
render() {
<Context.Provider value={this.props.store}>
{ this.props.children }
</Context.Provider>
}
}
componentDidMount () {
const { store } = this.props
store.dispatch(handleIitialData())
store.subscribe( () => this.forceUpdate())
}
render() {
const { store } = this.props
const { todos, goals, loading } = store.getState()
if(loading) { return <h3>Loading</h3> }
return(
<div>
< Todos todos={todos} store={this.props.store} />
< Goals goals={goals} store={this.props.store} />
</div>
)
}
}
Error
babel.min.js:27 Uncaught SyntaxError: Inline Babel script: Unexpected token (108:18)
106 | class App extends React.Component {
107 |
> 108 | const Context = React.createContext()
| ^
109 |
110 | class ConnectedApp extends React.Component {
111 | render() {
at r.l.raise (babel.min.js:27)
at r.c.unexpected (babel.min.js:27)
at r.c.expect (babel.min.js:27)
at r.m.parseMethod (babel.min.js:27)
at r.parseClassMethod (babel.min.js:28)
at r.m.parseClassBody (babel.min.js:27)
at r.m.parseClass (babel.min.js:27)
at r.m.parseStatement (babel.min.js:27)
at r.parseStatement (babel.min.js:27)
at r.m.parseBlockBody (babel.min.js:27)
This is because the syntax you are trying to use in that class is not in your babel configuration or supported by your engine (node / browser) (and it is just a proposal for the class public fields proposal).
You should rather add support for that syntax in your babel (stage-3), knowing that there is a risk for this syntax to not pass as a final feature of the language.
Or declare the Context constant outside the class (or inside the constructor of the class and then access it using this context).
Example (from the official documentation but adapted to your code):
// the Context is created outside App
const Context = React.createContext();
// Also ConnectedApp and Provider are OUTSIDE App (The console doesnt complain now, but it will after you fix the one with const)
class Provider extends React.Component {
render() {
<Context.Provider value={this.props.store}>
{ this.props.children }
</Context.Provider>
}
}
class App extends React.Component {
componentDidMount () {
const { store } = this.props
store.dispatch(handleIitialData())
store.subscribe( () => this.forceUpdate())
}
render() {
const { store } = this.props
const { todos, goals, loading } = store.getState()
if(loading) { return <h3>Loading</h3> }
return(
<div>
< Todos todos={todos} store={this.props.store} />
< Goals goals={goals} store={this.props.store} />
</div>
)
}
}
// ConnectedApp goes after App since it uses App internally
class ConnectedApp extends React.Component {
render() {
return (
<Context.Consumer>
{(store) => (
<App store={store}/>
)}
</Context.Consumer>
)
}
}
There are other React concept errors in your code, but since you are following a course I guess this is intentional by the moment.

How to retrieve ref from hoc translated component by i18next

I use i18next to translate my components in separate repo. Now I need to use method from one component in my main project.
This is example component:
class ExampleComponent extends React.Component {
constructor(props) {
this.state = {
text: '',
};
this.resetText = this.resetText.bind(this);
}
resetText() {
this.setState({
text: '',
});
}
render() {
<div/>
}
export default translate('components', { withRef: true })(ExampleComponent);
And now I need to use resetText in my main project, without translations it works fine
class MainComponent extends Component {
resetComponent() {
return () => this.exampleComponent.resetText();
}
renderExampleComponent() {
return (
<ExampleComponent
ref={(exampleComponent) => { this.exampleComponent = exampleComponent; }}
/>
);
}
render() {
return (
<div>
{this.resetComponent()}
</div>
);
}
I tried this.refs.exampleComponent.resetText(); but it not works.
In i18next documentation I found "withRef | store a ref to the wrapped component and access it by decoratedComponent.getWrappedInstance();" but where should I use this getWrappedInstance() to make it works?
Someone have experience with this?
Greetings
I think with the provided code, your ref returns a wrapper, so you need to call getWrappedInstance on it: this.refs.exampleComponent.getWrappedInstance().resetText();

Categories

Resources