I want to build my project with create react app. But, I encounter a blank page, when I run "yarn start" in project's directory. As others have said, I set "homepage": "." . but that does not work.
Some said router should be set to "hashrouter". Unfortunately, I don't understand how to do that.
This is my code that has used of context for building "themeSwitcher".
index.jsx:
import React from 'react';
import ReactDOM from 'react-dom';
import './app.css';
import {themeContext} from './context.js';
function themeSwitcher(){
return (
<themeContext.consumer>
{({Theme,changeTheme}) => (
<input
type="checkbox"
checked={Theme === "dark"}
onChange={() => changeTheme(Theme === "dark" ? "light" : "dark")}
/>
)}
</themeContext.consumer>
);
}
class app extends React.Component {
constructor(props) {
super(props);
this.state = {
Theme: "light",
changeTheme: this.changeTheme
};
}
changeTheme = (Theme) => {
this.setState({
Theme
});
};
render() {
return (
<themeContext.provider value={this.state}>
<div>
<p>this is a switcher theme</p>
<span>Dark mode</span>
<themeSwitcher />
</div>
</themeContext.provider>
);
}
}
ReactDOM.render(<app />, document.getElementById("root"));
context.js:
import React from "react";
export const themeContext = React.createContext({
Theme: "light",
changeTheme: () => {}
});
Why are the components written with small letters? Component names must begin with a capital letter.
If possible then present code from './context.js'
import React from 'react'
import ReactDOM from 'react-dom'
import './app.css'
import { ThemeContext } from './context.js'
function ThemeSwitcher() {
return (
<ThemeContext.Consumer>
{({ theme, toggleTheme }) => (
<input
type="checkbox"
checked={theme === 'dark'}
onChange={toggleTheme}
/>
)}
</ThemeContext.Consumer>
)
}
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
theme: 'light'
}
}
toggleTheme = () => {
this.setState(state => ({
theme:
state.theme === 'dark'
? 'light'
: 'dark',
}));
}
contextValue = {
theme: this.state.theme,
toggleTheme: this.toggleTheme
}
render() {
return (
<ThemeContext.Provider value={this.contextValue}>
<div>
<p>this is a switcher theme</p>
<span>Dark mode</span>
<ThemeSwitcher />
</div>
</ThemeContext.Provider>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'))
You can also use hooks and functional components. The code is cleaner.
import React, { useState } from 'react'
import ReactDOM from 'react-dom'
import './app.css'
import { ThemeContext } from './context.js'
const ThemeSwitcher = () => (
<ThemeContext.Consumer>
{({ theme, toggleTheme }) => (
<input
type="checkbox"
checked={theme === 'dark'}
onChange={toggleTheme}
/>
)}
</ThemeContext.Consumer>
)
const App = () => {
const [theme, setTheme] = useState('light')
const toggleTheme = () => setTheme(theme === 'dark' ? 'light' : 'dark')
const contextValue = {
toggleTheme,
theme,
}
return (
<ThemeContext.Provider value={contextValue}>
<div>
<p>this is a switcher theme</p>
<span>Dark mode</span>
<ThemeSwitcher />
</div>
</ThemeContext.Provider>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
context.js code
import React, { createContext } from "react";
export const ThemeContext = createContext({
theme: "light",
toggleTheme: () => {}
});
Related
I have my context Provider in my AppContext.tsx file
import { useState, useEffect, useContext, createContext } from 'react';
interface AppContextProps {
children: React.ReactNode
}
const themes = {
dark: {
backgroundColor: 'black',
color: 'white'
},
light: {
backgroundColor: 'white',
color: 'black'
}
}
const initialState = {
dark: false,
theme: themes.light,
toggle: () => { console.log('toggle from initalState') }
}
const AppContext = createContext(initialState);
export function AppWrapper({ children }: AppContextProps) {
const [dark, setDark] = useState(false) // Default theme is light
// On mount, read the preferred theme from the persistence
useEffect(() => {
const isDark = localStorage.getItem('dark') === 'true'
setDark(isDark)
}, [dark])
// To toggle between dark and light modes
const toggle = () => {
console.log('toggle from AppWrapper')
const isDark = !dark
localStorage.setItem('dark', JSON.stringify(isDark))
setDark(isDark)
}
const theme = dark ? themes.dark : themes.light
return (
<AppContext.Provider value={{ theme, dark, toggle }}>
{children}
</AppContext.Provider>
);
}
export function useAppContext() {
return useContext(AppContext);
}
I insert Appwrapper into my _app.tsx file
import type { ReactElement, ReactNode } from 'react'
import type { NextPage } from 'next'
import type { AppProps } from 'next/app'
import { AppWrapper } from '../contexts/AppContext';
import 'normalize.css/normalize.css';
import '../styles/globals.scss'
type NextPageWithLayout = NextPage & {
getLayout?: (page: ReactElement) => ReactNode
}
type AppPropsWithLayout = AppProps & {
Component: NextPageWithLayout
}
const App = ({ Component, pageProps }: AppPropsWithLayout) => {
const getLayout = Component.getLayout ?? ((page) => page)
return getLayout(
<AppWrapper>
<Component {...pageProps} />
</AppWrapper>
)
}
export default App
And I call and import the useAppContext hook to use it in my Header component
import styles from './header.module.scss'
import { useAppContext } from '../contexts/AppContext'
const Header = () => {
const { theme, toggle, dark } = useAppContext()
return (
<header className={styles.header}>
<nav className={styles.nav}>
<div onClick={toggle} className={styles.switchWrap}>
<label className={styles.switch}>
<input type="checkbox" />
<span className={styles.slider}></span>
</label>
</div>
</nav>
</header>
)
}
export default Header
However, my toggle function logs toggle from initalState instead of toggle from AppWrapper.
The following code below
const { theme, toggle, dark } = useAppContext()
gets data from initialState
const initialState = {
dark: false,
theme: themes.light,
toggle: () => { console.log('toggle from initalState') }
}
instead of what I pass into the value props shown below
<AppContext.Provider value={{ theme, dark, toggle }}>
{children}
</AppContext.Provider>
How do I correctly pass the value props data into my component instead of the data from initialState?
Because my app was using per-page layouts, I inserted AppWrapper in my layout file instead of _app.tsx and everything works
import Header from './header'
import Footer from './footer'
import { AppWrapper } from '../contexts/AppContext';
interface LayoutProps {
children: React.ReactNode
}
const Layout = ({ children }: LayoutProps) => {
return (
<AppWrapper>
<Header />
{children}
<Footer />
</AppWrapper>
)
}
export const getLayout = (page: LayoutProps) => <Layout>{page}</Layout>;
export default Layout
index.js is as follows:
import './index.css';
import { render } from "react-dom";
const { React } = require('react');
const { App } = require('./App');
const { serviceWorker } = require('./serviceWorker');
ReactDOM.render( < App / > , document.getElementById('root'));
serviceWorker.unregister();
and app.jsx is as follows:
import React from "react";
import"./App.scss";
import { Login, Register } from "./components/login/index";
export class App extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoginActive : true
};
}
componentDidMount() {
this.rightSide.classList.add("right");
}
changeState() {
const { isLoginActive } = this.state;
if (isLoginActive) {
this.rightSide.classList.remove("right");
this.rightSide.classList.add("left");
}else{
this.rightSide.classList.remove("left");
this.rightSide.classList.add("right");
}
this.setState(prevState => ({
isLoginActive: !prevState.isLoginActive
}));
}
render() {
const { isLoginActive } = this.state;
const current = isLoginActive ? "Register" : "Login";
const currentActive = isLoginActive ? "Login" : "Register";
return (
<div className="App">
<div className = "login">
<div className = "container" ref={ref => (this.container = ref)}>
{isLoginActive && (
<Login containerRef={ref => (this.current = ref)} />
)}
{!isLoginActive && (
<Register containerRef={ref => {this.current = ref}} />
)}
</div>
<RightSide
current={current}
currentActive={currentActive}
containerRef={ref => (this.rightSide = ref)}
onClick={this.changeState.bind(this)}
/>
</div>
</div>
);
}
}
const RightSide = props => {
return (
<div
className = "right-side"
ref={props.containerRef}
onClick={props.onClick}
>
<div className="inner-container">
<div className="text">{props.current}</div>
</div>
</div>
);
};
export default App;
on "npm start" I get the following error:
Failed to Compile
src\index.js
Line 14:1: 'ReactDOM' is not defined no-undef
Search for the keywords to learn more about each error.
my React and ReactDOM are up to date and compatible.
I am unable to figure out the solution to this issue.
Any kind of assistance would be appreciated.
Thanks!!
You have to correct the import statement of react-dom like below.
import ReactDOM from "react-dom";
you have to import it:
https://reactjs.org/docs/react-dom.html
import ReactDOM from 'react-dom'
Check for typo.
I got the same error, it was because of the difference between ReactDom and ReactDOM.
import ReactDom from 'react-dom';
ReactDOM.render
In my React project I am using a library of components:
<customcomponent />
However that library components does not support passing className. Therefore I can not use this method to apply style to the component like this:
<customcomponent className='mystyles' />
How to apply style to the component in such circumstance?
You would have to create a wrapper component that wraps your component with a div that accepts a className.
const DoesntSupportClassNameWrapper = (props) => {
const { className, style, ...rest } = props;
return (
<div className={className} style={style}>
<DoesntSupportClassName {...rest} />
</div>
);
};
Full example
Edit # CodeSandbox
App.jsx
import React from "react";
import PropTypes from "prop-types";
import { DoesntSupportClassName, SupportsClassName } from "./components";
import "./styles.css";
const styles = {
heading: {
color: "red"
}
};
const DoesntSupportClassNameWrapper = (props) => {
const { className, style, ...rest } = props;
return (
<div className={className} style={style}>
<DoesntSupportClassName {...rest} />
</div>
);
};
DoesntSupportClassNameWrapper.propTypes = {
className: PropTypes.string,
style: PropTypes.object
};
const App = () => {
return (
<div className="App">
<SupportsClassName text="Heading 1" className="heading" />
<DoesntSupportClassName
text="Heading 2"
className="heading"
style={styles.heading}
/>
<DoesntSupportClassNameWrapper text="Heading 3" className="heading" />
<DoesntSupportClassNameWrapper text="Heading 4" style={styles.heading} />
</div>
);
};
export default App;
DoesntSupportClassName.jsx
import React from "react";
import PropTypes from "prop-types";
const DoesntSupportClassName = (props) => {
const { text } = props;
return <h1>{text}</h1>;
};
DoesntSupportClassName.propTypes = {
text: PropTypes.string
};
export default DoesntSupportClassName;
SupportsClassName.jsx
import React from "react";
import PropTypes from "prop-types";
const SupportsClassName = (props) => {
const { text, className } = props;
return <h1 className={className}>{text}</h1>;
};
SupportsClassName.propTypes = {
className: PropTypes.string,
text: PropTypes.string
};
export default SupportsClassName;
as mentioned in the title I'm trying to set up some test for <Search /> component, in particular I want to test the useState hooks.
After mocking the Redux store and creating a shallow wrapper I tried to simulate an input from the child component DisplaySearchBar but apparently I cannot even mamage to select it.
That's the error I get:
Method “props” is meant to be run on 1 node. 0 found instead.
Here's Search.js
import React, { useState } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { handleScriptLoad } from '../../../helpers/Autocomplete';
import { getRestaurants, setAlert } from '../../../actions/restaurantAction';
import DisplaySearchBar from '../../layout/DisplaySearchBar/DisplaySearchBar';
import styles from './Search.module.scss';
const Search = ({ getRestaurants, setAlert }) => {
const [where, setWhere] = useState('');
const [what, setWhat] = useState('');
const [sortBy, setSortBy] = useState('rating');
const sortByOptions = {
'Highest Rated': 'rating',
'Best Match': 'best_match',
'Most Reviewed': 'review_count',
};
// give active class to option selected
const getSortByClass = (sortByOption) => {
if (sortBy === sortByOption) {
return styles.active;
} else {
return '';
}
};
// set the state of a sorting option
const handleSortByChange = (sortByOption) => {
setSortBy(sortByOption);
};
//handle input changes
const handleChange = (e) => {
if (e.target.name === 'what') {
setWhat(e.target.value);
} else if (e.target.name === 'where') {
setWhere(e.target.value);
}
};
const onSubmit = (e) => {
e.preventDefault();
if (where && what) {
getRestaurants({ where, what, sortBy });
setWhere('');
setWhat('');
setSortBy('best_match');
} else {
setAlert('Please fill all the inputs');
}
};
// displays sort options
const renderSortByOptions = () => {
return Object.keys(sortByOptions).map((sortByOption) => {
let sortByOptionValue = sortByOptions[sortByOption];
return (
<li
className={getSortByClass(sortByOptionValue)}
key={sortByOptionValue}
onClick={() => handleSortByChange(sortByOptionValue)}
>
{sortByOption}
</li>
);
});
};
return (
<DisplaySearchBar
onSubmit={onSubmit}
handleChange={handleChange}
renderSortByOptions={renderSortByOptions}
where={where}
what={what}
handleScriptLoad={handleScriptLoad}
/>
);
};
Search.propTypes = {
getRestaurants: PropTypes.func.isRequired,
setAlert: PropTypes.func.isRequired,
};
export default connect(null, { getRestaurants, setAlert })(Search);
DisplaySearchBar.js
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { clearSearch } from '../../../actions/restaurantAction';
//Import React Script Libraray to load Google object
import Script from 'react-load-script';
import Fade from 'react-reveal/Fade';
import Alert from '../Alert/Alert';
import styles from './DisplaySearchBar.module.scss';
const DisplaySearchBar = ({
renderSortByOptions,
onSubmit,
where,
handleChange,
what,
handleScriptLoad,
restaurants,
clearSearch,
}) => {
const googleUrl = `https://maps.googleapis.com/maps/api/js?key=${process.env.REACT_APP_GOOGLE_API_KEY}&libraries=places`;
// {googleUrl && <Script url={googleUrl} onLoad={handleScriptLoad} />}
return (
<section className={styles.searchBar}>
<form onSubmit={onSubmit} className={styles.searchBarForm}>
<legend className="title">
<Fade left>
<h1>Where are you going to eat tonight?</h1>
</Fade>
</legend>
<Fade>
<fieldset className={styles.searchBarInput}>
<input
type="text"
name="where"
placeholder="Where do you want to eat?"
value={where}
onChange={handleChange}
id="autocomplete"
/>
<input
type="text"
name="what"
placeholder="What do you want to eat?"
onChange={handleChange}
value={what}
/>
<div className={styles.alertHolder}>
<Alert />
</div>
</fieldset>
<fieldset className={styles.searchBarSubmit}>
<input
id="mainSubmit"
className={`${styles.myButton} button`}
type="submit"
name="submit"
value="Search"
></input>
{restaurants.length > 0 && (
<button
className={`${styles.clearButton} button`}
onClick={clearSearch}
>
Clear
</button>
)}
</fieldset>
</Fade>
</form>
<article className={styles.searchBarSortOptions}>
<Fade>
<ul>{renderSortByOptions()}</ul>
</Fade>
</article>
</section>
);
};
DisplaySearchBar.propTypes = {
renderSortByOptions: PropTypes.func.isRequired,
where: PropTypes.string.isRequired,
handleChange: PropTypes.func.isRequired,
what: PropTypes.string.isRequired,
handleScriptLoad: PropTypes.func.isRequired,
restaurants: PropTypes.array.isRequired,
clearSearch: PropTypes.func.isRequired,
};
const mapStatetoProps = (state) => ({
restaurants: state.restaurants.restaurants,
});
export default connect(mapStatetoProps, { clearSearch })(DisplaySearchBar);
And Search.test.js
import React from 'react';
import { mount } from 'enzyme';
import configureStore from 'redux-mock-store';
import { Provider } from 'react-redux';
import Search from '../Search';
import DisplaySearchBar from '../../../layout/DisplaySearchBar/DisplaySearchBar';
const mockStore = configureStore();
const initialState = {
restaurants: { restaurants: ['foo'], alert: null },
};
describe('Search', () => {
test('renders withut errors', () => {
const store = mockStore(initialState);
const wrapper = mount(
<Provider store={store}>
<Search setAlert={jest.fn()} getRestaurants={jest.fn()} />
</Provider>
);
wrapper.find(DisplaySearchBar).props();
});
});
Thanks for your help!
shallow doesn't work for react-redux new versions (>= 6).
Use mount instead:
const wrapper = mount( // <-- changed shallow to mount.
<Provider store={store}>
<Search {...props} />
</Provider>
);
Run It On Sandbox (Use tests tab to run tests.)
Try to mount it like this:
const wrapper = shallow(
<Provider store={store} />
<Search setAlert=jest.fn() getRestaurants=jest.fn() />
</Provider>
);
I want to use functions like useMediaQuery in class based components.
How can I make achieve this ?
import React from 'react';
import clsx from 'clsx';
import { withStyles ,withTheme ,withMediaQuery } from '#material-ui/styles';
import { useMediaQuery } from '#material-ui/core'
class Main extends React.Component{
render(){
const { children ,classes,theme} = this.props;
const isDesktop = useMediaQuery(theme.breakpoints.up('lg'),{
defaultMatches :true
});
return (
<div className ={clsx({
[classes.root] : true,
[classes.shiftContent] : isDesktop
})}>
<main>
{children}
</main>
</div>
);
}
}
You can wrap your useMediaQuery and the rest of the logic in another component:
const WithClasses = ({ children, theme, classes }) => {
const isDesktop = useMediaQuery(theme.breakpoints.up("lg"), {
defaultMatches: true
});
return (
<div
className={clsx({
[classes.root]: true,
[classes.shiftContent]: isDesktop
})}
>
{children}
</div>
);
};
Then use it in your layout like this:
class Main extends React.Component {
render() {
const { children, classes, theme } = this.props;
return (
<WithClasses theme={theme} classes={classes}>
<main>{children}</main>
</WithClasses>
);
}
}