For this input component, I want to make it as a basic component for other components.
import React, { InputHTMLAttributes } from "react";
import styled, { css } from "styled-components";
export const TextField: React.FC<InputHTMLAttributes<HTMLInputElement>> = (
props
) => {
return (
<Input type="text" {...props} />
);
};
const Input = styled.input`
${({ theme }) => css`
width: 100%;
color: ${theme.color.text};
`}
`;
This is a customized one and add some styles:
import React from "react";
import styled, { css } from "styled-components";
import { TextField } from "~/components";
export type ProductFieldProps = {
name: string;
value?: string;
};
type ProductFieldTypeProps = {
textAlign: string;
};
export const ProductField: React.FC<ProductFieldProps> = ({
name,
value,
}) => {
return (
<StyledTextField textAlign="center">
<TextField name={name} value={value} />
</StyledTextField>
);
};
const StyledTextField = styled(TextField)`
${({ theme }) => css<ProductFieldTypeProps>`
&:hover {
border-color: ${theme.color.hover};
}
`}
`;
When use the new component ProductField,
# pages/index.tsx
import { ProductField } from "~/components";
...
return {
<>
<ProductField name="aaa" value="bbb" />
</>
}
I got a server error:
Error: input is a void element tag and must neither have children nor use dangerouslySetInnerHTML.
How to set correctly?
You are using styled-components to style your Input component.
You can see on this line that you are applying styles to the TextField component:
const StyledTextField = styled(TextField)
And yet in the new ProductField component you import both, the styled version and the original one and nest them in each other:
<StyledTextField textAlign="center">
<TextField name={name} value={value} />
</StyledTextField>
This essentially means you are nesting two input field into each other, which is not allowed and hence the error:
Error: input is a void element tag and must neither have children nor use dangerouslySetInnerHTML.
One way to approach this is to simply remove the nested TextField like so, and apply all the props to the styled one instead:
<StyledTextField textAlign="center" name={name} value={value} />
Another solution would be to just export and use the styled version directly because the additional wrapper seems redundant.
More info on how to style custom components with styled-components
Related
I'm trying to learn ReactJS and now I'm making a small project that consists of a button that every time I click it, it will show an html element (as I still couldn't get the button to work, I haven't created the element yet, so the element it should create is an h1), but when I click the button nothing happens, could anyone help me? (sorry for any spelling mistakes, I'm still learning English)
This is my code:
import Container from "./App.style";
import Button from "./Components/Button/Button.jsx";
const App = () => {
const createElement = () => {
return(
<h1>Hey</h1>
)
}
return (
<>
<Container>
<h1>Criador De Elementos</h1>
<Button onClick={ createElement }>Criar Elemento</Button>
</Container>
</>
);
}
export default App;
import React from "react";
import StylesButton from "./Button.style";
const Button = ( {onClick, children} ) => {
return (
<StylesButton onClick={ onClick }> { children } </StylesButton>
);
}
export default Button;
Looks like you your code is correct. But my question is where you want to that h1 element. If you want h1 to Your DOM than you must create an h1 to your DOM and control value of h1 by using state (by Using useState Hook).
if you want to render your h1 element then you can do like this.
import {useState} from 'react';
import Container from "./App.style";
import Button from "./Components/Button/Button.jsx";
const App = () => {
const [heading,setHeading] = useState("");
const createElement = () => {
setHeading("Hey");
}
return (
<>
<Container>
<h1>Criador De Elementos</h1>
<Button onClick={ createElement }>Criar Elemento</Button>
<h1>{heading}</h1>
</Container>
</>
);
}
export default App;
import React from "react";
import StylesButton from "./Button.style";
const Button = ( {onClick, children} ) => {
return (
<StylesButton onClick={ onClick }> { children } </StylesButton>
);
}
export default Button;
Codesandbox here
I am using context to the state of various components, in this case, which checkbox is checked, and am using that component to mutate that component state. However, by clicking only one checkbox, all four components (checkboxes) rerender. How can I prevent this rerendering when hooking into and mutating context? Thanks.
index.tsx:
import * as React from "react";
import ReactDOM from "react-dom";
import { ContextProvider, useMyContext } from "./context";
import "./styles.css";
const Checkbox: React.FC<{ id: number }> = ({ id }) => {
React.useEffect(() => console.log(`Checkbox ${id} Render`));
const { setValue, value } = useMyContext();
return (
<input
onClick={() => setValue(id)}
checked={id === value}
type="checkbox"
/>
);
};
const Container: React.FC = () => {
React.useEffect(() => console.log("Container Render"));
return (
<div>
{[0, 1, 2, 3].map(id => (
<Checkbox id={id} />
))}
</div>
);
};
const App: React.FC = () => {
return (
<ContextProvider>
<Container />
</ContextProvider>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
context.tsx:
import * as React from "react";
interface ContextState {
setValue: (id: number) => void;
value: number;
}
const initialContextState: ContextState = {
setValue: () => {},
value: 0
};
const Context = React.createContext<ContextState>(initialContextState);
export const ContextProvider: React.FC = (props: object) => {
const [value, setValue] = React.useState<number>(0);
return (
<Context.Provider
value={{
setValue,
value
}}
{...props}
/>
);
};
export const useMyContext = (): ContextState => React.useContext(Context);
You can't prevent the rerender of the context-consuming component (Checkbox in your example) - the forced rerender when the context value is updated is just how the Context API works, you can't apply any 'selectivity' to it.
What you can do is take the expensive part of the actual content of that consuming component, extract it into a child component wrapped with React.memo (or inline it as a chunk of JSX wrapped in a useMemo hook - docs), get the values you need from the context and pass those as props to that child component / dependencies to the useMemo section.
The built in memoisation will then 'just work' and will not rerender the child component (the expensive part) for the checkboxes whose actual value doesn't update, as the relevant props will not change.
Using memoisation like this can solve any actual performance issues you need to address, but just a reminder that 90% of the time, this stuff just doesn't matter. Test and determine performance is actually an issue before you refactor your components in this way. It's usually simply not worth the extra indirection to solve a non-problem!
Here you can find a similar example,
using this library react-hooks-in-callback you can filter out all unnecessary re-renders
check the result Here
I want text entry to be highlighted with different colors depending on the character entered.
My hunch is that it is possible to do this by adding <span> elements with the appropriate styling inside of a contenteditable div.
Is there a better way?
Hi Please check this example. I used material-ui
import React, {useState} from 'react';
import Box from '#material-ui/core/Box';
import TextField from "#material-ui/core/TextField";
export default function BackgroundColor() {
const [text, setText] = useState('');
const [color, setColor] = useState('');
function changeHandler(event) {
setText(event.target.value);
if(event.target.value.toLowerCase() === 'a'){
setColor("primary.main");
}
else if(event.target.value.toLowerCase() === 'b'){
setColor("secondary.main");
}
else if(event.target.value.toLowerCase() === 'c'){
setColor("error.main");
}
else{
setColor("info.main");
}
}
return (
<div>
<TextField id="standard-basic" label="Standard" helperText="Type A or B or C" onChange={changeHandler} />
<Box color={color}>
{text}
</Box>
</div>
);
}
Depending on the complexity, you can also consider using Ace Editor
import React, { useState } from "react";
import AceEditor from "react-ace-builds";
import "./yamlHighlightRules";
import "ace-builds/src-noconflict/theme-github";
import "ace-builds/src-noconflict/ace";
import "./styles.css";
export default function App() {
const [text, setText] = useState("This is the most amazing initial text.");
const handleChange = input => {
setText(input);
};
return (
<div className="App">
<AceEditor
mode="yaml"
theme="github"
name="editor"
fontSize={15}
showPrintMargin={true}
showGutter={false}
highlightActiveLine={false}
value={text}
onChange={handleChange}
setOptions={{
showLineNumbers: true,
tabSize: 2,
readOnly: false
}}
height={"600px"}
width={"100%"}
/>
</div>
);
}
And then I edited an existing set of yaml highlighting rules, it's easier not starting from scratch.
import ace from "ace-builds/src-min-noconflict/ace";
import "ace-builds/src-noconflict/mode-text";
ace.define("ace/mode/yaml_highlight_rules", [], function(require, exports) {
const oop = require("../lib/oop");
const TextHighlightRules = require("./text_highlight_rules")
.TextHighlightRules;
const YamlHighlightRules = function() {
this.$rules = {
start: [
{
token: "highlight",
regex: /amazing/
}
]
};
this.normalizeRules();
};
oop.inherits(YamlHighlightRules, TextHighlightRules);
exports.YamlHighlightRules = YamlHighlightRules;
});
The token is your css class. Just add the prefix ace_ to it. Then you define a regular expression to determine what gets that class.
.ace_highlight {
background-color: yellow;
}
Here is a codesandbox where you can see it working.
And here are the docs on defining your own modes.
I was having trouble passing the to to the Link component of react-router-dom in the Tab element of the material-ui core.
I finally came up with this solution:
import * as React from 'react';
import Tabs from '#material-ui/core/Tabs';
import Tab from '#material-ui/core/Tab';
import { Link } from 'react-router-dom';
interface Props {
title?: string;
}
interface State {
value: number;
}
class NavButtons extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { value: 0 };
}
public handleOnChange = (event: any, value: number) => {
this.setState({ value })
}
public render() {
const {value} = this.state
return (
<Tabs value={value} onChange={this.handleOnChange} >
<Tab label="Home" component={Link} {...{to:"/"} as any} />
<Tab label="Contact" component={Link} {...{to:"/contact/"} as any} />
</Tabs>
)
}
}
export default NavButtons
The only problem is that I can't seem to find out what ...{} as any does in the documentation of material-ui or react.
Can someone explain this to me? I see a lot of React programmers use it but I have no idea what it does exactly.
typescript. what is mean: (this as any)
Looks like it's a TypeScript syntax. Adding as any could remove the type checking of {to:"/"} so it won't cause any warning/error.
I am looking for a solution in order to still be able to use Link from react-router instead of a when testing href attribute value.
Indeed, I have some components which change of route according to the context. However, when I am testing the href attribute value, the only thing returned is null.
However, when I use an a, it returns me the expected value.
Here is an failing test:
import React from 'react';
import {Link} from 'react-router';
import TestUtils from 'react-addons-test-utils';
import expect from 'must';
const LINK_LOCATION = '/my_route';
class TestComponent extends React.Component {
render() {
return (
<div>
<Link className='link' to={LINK_LOCATION}/>
<a className='a' href={LINK_LOCATION}/>
</div>
);
}
}
describe('Url things', () => {
it('should return me the same href value for both link and a node', () => {
const test_component = TestUtils.renderIntoDocument(<TestComponent/>);
const link = TestUtils.findRenderedDOMComponentWithClass(test_component, 'link');
const a = TestUtils.findRenderedDOMComponentWithClass(test_component, 'a');
expect(link.getAttribute('href')).to.eql(a.getAttribute('href'));
});
});
Output: AssertionError: null must be equivalent to "/my_route"
knowbody from React-router answered to see how they test Link, but they do not have dynamic context which can change value of the href attribute.
So I have done something like that:
class ComponentWrapper extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
set_props(props) {
this.setState({props});
}
render() {
if (this.state.props) {
return <Component {...this.state.props}/>;
}
return null;
}
}
But now, from my component helper:
render_into_document() {
const full_component_props = {
location: this.location,
widget_categories: this.widget_categories
};
node = document.createElement('div');
this.component = render((
<Router history={createHistory('/')}>
<Route path='/' component={ComponentWrapper} />
</Router>
));
this.component.set_props(full_component_props);
return this;
}
I am not able to lay hand on this.component in order to changes props. How could I do that?
I just looked at how react-router tests <Link /> and came up with this for my case:
import test from 'ava'
import React from 'react'
import { render } from 'enzyme'
import { Router, Route } from 'react-router'
import createHistory from 'history/lib/createMemoryHistory'
import SkipToXoom from '../skip-to-xoom'
test('the rendered button redirects to the proper URL when clicked', t => {
const toCountryData = { countryName: 'India', countryCode: 'IN' }
const div = renderToDiv({ toCountryData, disbursementType: 'DEPOSIT', userLang: 'en_us' })
const { attribs: { href } } = div.find('a')[0]
t.true(href.includes(encodeURIComponent('receiveCountryCode=IN')))
t.true(href.includes(encodeURIComponent('disbursementType=DEPOSIT')))
t.true(href.includes(encodeURIComponent('languageCode=en')))
})
/**
* Render the <SkipToXoom /> component to a div with the given props
* We have to do some fancy footwork with the Router component to get
* the Link component in our SkipToXoom component to render out the href
* #param {Object} props - the props to apply to the component
* #returns {Element} - the div that contains the element
*/
function renderToDiv(props = {}) {
return render(
<Router history={createHistory('/')}>
<Route path="/" component={() => <SkipToXoom {...props} userLang="en" />} />
</Router>
)
}
I hope that's helpful!