How can i share state between AppContainer and Home component?
For example, i want put results object in the state of AppContainer for pass it to all other components.
In this app i'm not using Redux.
I've tried to use React.cloneElement in AppContainer but generate an error:
Element type is invalid: expected a string (for built-in components)
or a class/function (for composite components) but got: undefined. You
likely forgot to export your component from the file it's defined in.
index.js
<Router history={browserHistory}>
<AppContainer>
<Route path="/" component={Home} />
<Route path="search" component={Search} />
</AppContainer>
</Router>
AppContainer.js
render() {
return (
<div className="containerApp">
{this.props.children}
</div>
);
}
React.cloneElement is the right way to go. For example:
render() {
const childProps = {
prop1: this.state.prop1
}
return (
<div className="containerApp">
{React.cloneElement(this.props.children, childProps)}
</div>
);
}
If this gives you the error you were previously receiving then the only reason I can think is that AppContainer is being rendered without any children prior to any route match. You could mitigate this either by conditionally rendering in AppContainer:
render() {
const childProps = {
prop1: this.state.prop1
}
const { children } = this.props;
return (
<div className="containerApp">
{children && React.cloneElement(children, childProps)}
</div>
);
}
Related
I'm currently trying to set a globalstate into my app. It's just about counting points, so a global "score".
Context seemed easy and I followed some tutorials, but it keeps sending errors.
In my app, I am using React and BrowserRouter to switch between pages.
App.jsx You can see, I tried to wrap with the Store Component
export default function App() {
return (
<main>
<Routes>
<Store>
<Route path="/" element={<Welcome />} />
<Route path="/whoareyou" element={<WhoAreYou />} />
<Route path="/questionone" element={<QuestionOne />} />
<Route path="/questiontwo" element={<QuestionTwo />} />
<Route path="/questionthree" element={<QuestionThree />} />
<Route path="/result" element={<Result />} />
</Store>
</Routes>
</main>
);
}
Points.js This is my Context file
import React, { useState, createContext } from "react";
const initialState = { points: 0 };
export const Context = createContext(initialState);
export const Store = ({ children }) => {
const [state, setState] = useState(initialState);
return (
<Context.Provider value={[state, setState]}>{children}</Context.Provider>
);
};
Component Here is my component, where I want to count a 1 into the "points" Score
export default function WhoAreYou() {
const [state, setState] = useContext(Context);
return (
<StyledSection variant="center">
<StyledNavLink to="/questionone">
<StyledButton
type="button"
variant="next-button"
onClick={() => {
console.log(state.points);
}}
>
NEXT
</StyledButton>
</StyledNavLink>
</StyledSection>
);
}
Errors
This are the error messages:
undefined is not an object (evaluating 'element.type.name')
Warning: React.jsx: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
Check the render method of App.
App
Router
BrowserRouter
To See the whole code, you can see this CodeSandBox:
https://codesandbox.io/s/empty-https-gpkq0l?file=/src/components/pages/WhoAreYou.jsx:581-1422
I would be very thankful if you guys could help!!
Import Store as named import (you exported it as named export)
import { Store } from "../src/components/context/Points";
I have this piece of code:
const defaultValue = new Api()
const ApiContext = React.createContext(defaultValue);
const ApiProvider = ApiContext.Provider;
const ApiConsumer = ApiContext.Consumer;
const withApi = (Enhanced: any) => {
return (
<ApiConsumer>
{api => {
return <Enhanced api={api}/>;
}}
</ApiConsumer>
)
}
export default ApiContext;
export {ApiContext, ApiProvider, ApiConsumer, withApi};
And in my app, I have something like this:
const api = new ApiManager({...});
ReactDOM.hydrate(
<Provider store={store}>
<BrowserRouter>
<ApiProvider value={api}>
<Main />
</ApiProvider>
</BrowserRouter>
</Provider>, document.querySelector('#app')
);
But this line return <Enhanced api={api}/>; causes these errors:
1.
Warning: React.createElement: type is invalid -- expected a string
(for built-in components) or a class/function (for composite
components) but got: . Did you accidentally
export a JSX literal instead of a component?
2.
Uncaught Invariant Violation: Element type is invalid: expected a
string (for built-in components) or a class/function (for composite
components) but got: object.
3.
Uncaught (in promise) Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.
Check the render method of `Context.Consumer`.
What I'm I doing wrong here?
How can I pass the api prop to an enhanced component?
[EDIT]
This is how I'm calling my component:
App.tsx
class App extends React.Component {
render() {
return (
<React.Fragment>
<Switch>
{routes.map(({ path, exact, component: C }) => {
return <Route
key={path}
path={path}
exact={exact}
render={(props) => {
return withApi(<C {...props} />);
}} />
})}
</Switch>
</React.Fragment>
)
}
}
You haven't written your withApi HOC correctly. It should return a functional component instead of JSX
const withApi = (Enhanced: any) => {
return (props) => {
return (
<ApiConsumer>
{api => {
return <Enhanced {...props} api={api}/>;
}}
</ApiConsumer>
)
}
}
and use it like
class App extends React.Component {
render() {
return (
<React.Fragment>
<Switch>
{routes.map(({ path, exact, component: C }) => {
const Comp = withApi(C);
return <Route
key={path}
path={path}
exact={exact}
render={(props) => {
return <Comp {...props}/>
}} />
})}
</Switch>
</React.Fragment>
)
}
}
So I've been learning React for a while, but one of the key's in what I have yet succeeded is passing states.
What I'm trying to do is passing a state from my App.js to a component rendered via route. Unfortunately, in MyComponent.js, I get error message 'lang is undefined'.
Can someone guide me to a solution?
This is what I have so far:
App.js
class App extends Component {
constructor (props) {
super (props)
this.state = {
language: 'en'
}
}
render() {
return (
<BrowserRouter>
<div className='App'>
<Switch>
<Route
exact path='/' render={(props) => <MyComponent lang={this.state.language} />}
/>
<Route exact path='/privacy/policy' component={Policy} />
<Route path='*' component={NotFound} />
</Switch>
</div>
</BrowserRouter>
);
} } export default App;
MyComponent.js
export class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {
lang : {lang}
}
}
render () {
return (
<div className='searchBar'>
<p>Current language is: {this.state.lang}</p>// HERE COMES THE STATE
</div>
);
}}
You are trying to pass props down to MyComponent component but you are trying to render it in SearchBar component. Rename SearchBar to MyComponent in the first route of your App.js.
Also, in MyComponent.js, remove the lang property from state and change:
<p>Current language is: {this.state.lang}</p>
to
<p>Current language is: {this.props.lang}</p>
I'm using react router V4, when i declare a Route i want wrap my component inside a High Order component, if I use HOC in
export default hoc(Component)
Then I put the component in the render prop, it works.
When I do this
`<Route exact path="/projects" render={(props) => (withNavHOC(<ProjectsContainer {...props}/>))} />`
It returns this error:
Uncaught Error: Route.render(): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.
Why it's happening? My Hoc works fine, it returns a valid react component:
`
const withNavHOC = (WrappedComponent) => {
return class extends React.Component{
render(){
if(this.props.match.params.id){
console.log("Here");
return(
<div>
<ProjectMenu/>
<WrappedComponent {...this.props}/>
</div>)
}
return(
<div>
<Navigation/>
<WrappedComponent {...this.props}/>
</div>
)
}
}
};`
To HOC which is of course a simple function, we must pass "Component", not <Component/>.
I agree there are multiple questions asked for the same. I did a lot of research for a few hours, however, I couldnt resolve this easy looking error. According to How to pass props to {this.props.children} post, i understand the easy use of React.cloneElement and React.Children.
Following is my Parent Class:
class AboutPage extends React.Component {
constructor(props, context){
super(props, context);
this.state = {
details: "details"
}
}
render() {
const childrenWithProps = React.Children.map(this.props.children,
(child) => {
React.cloneElement(child, {
details: this.state.details
})
}
);
return (
<div className="jumbotron">
<h1>About</h1>
<p>This application uses React, Redux, React Router and other libs for our EducationOne Project</p>
<Link to="/about/Owner">
<Button color="primary">test</Button>
</Link>
{childrenWithProps}
</div>
);
}
}
AboutPage.PropTypes = {
children: PropTypes.object.isRequired
};
Following is my child component:
const Owner = (props) => {
return (
<div>Owner details: {props.details}</div>
);
};
Instead of importing the child or parent, i nested the route in my routes.js to create a child for aboutPage:
export default (
<Route path="/" component={App}>
<IndexRoute component={Login} />
<Route path="home" component={HomePage}/>
<Route path="about" component={AboutPage}>
<Route path="omkar" components={Omkar} />
</Route>
<Route path="courses" component={CoursesPage}>
{/*<IndexRoute components={CourseDetailsAndList}/>*/}
</Route>
</Route>
);
However, I dont see any error or any message in the console, nor the child component loaded when i click the button to load the child component.
Any help will be really appreciated.
The problem is in your map function. The callback arrow function has block body with brackets and so you need to explicitly return your cloned element with the return keyword.
In a concise body, only an expression is needed, and an implicit
return is attached. In a block body, you must use an explicit return
statement.
const childrenWithProps = React.Children.map(this.props.children, child => {
return React.cloneElement(child, {
details: this.state.details
});
});