plu*_*us- 803 javascript reactjs react-jsx
我正在尝试找到正确的方法来定义一些可以通用方式使用的组件:
<Parent>
<Child value="1">
<Child value="2">
</Parent>
Run Code Online (Sandbox Code Playgroud)
当然,父级和子级组件之间的渲染有一个逻辑,你可以想象<select>并<option>作为这种逻辑的一个例子.
对于问题,这是一个虚拟实现:
var Parent = React.createClass({
doSomething: function(value) {
},
render: function() {
return (<div>{this.props.children}</div>);
}
});
var Child = React.createClass({
onClick: function() {
this.props.doSomething(this.props.value); // doSomething is undefined
},
render: function() {
return (<div onClick={this.onClick}></div>);
}
});
Run Code Online (Sandbox Code Playgroud)
问题是每当你{this.props.children}用来定义包装器组件时,如何将一些属性传递给它的所有子组件?
Dom*_*nic 876
您可以使用React.Children迭代子项,然后使用React.cloneElement使用新的props(浅合并)克隆每个元素,例如:
const Child = ({ doSomething, value }) => (
<div onClick={() => doSomething(value)}>Click Me</div>
);
class Parent extends React.PureComponent {
doSomething = value => {
console.log('doSomething called by child with value:', value);
}
render() {
const childrenWithProps = React.Children.map(this.props.children, child =>
React.cloneElement(child, { doSomething: this.doSomething })
);
return <div>{childrenWithProps}</div>
}
};
ReactDOM.render(
<Parent>
<Child value="1" />
<Child value="2" />
</Parent>,
document.getElementById('container')
);
Run Code Online (Sandbox Code Playgroud)
小提琴:https://jsfiddle.net/2q294y43/2/
小智 359
要获得更简洁的方法,请尝试:
<div>
{React.cloneElement(this.props.children, { loggedIn: this.state.loggedIn })}
</div>
Run Code Online (Sandbox Code Playgroud)
注意:这只有在有一个子节点时才有效,并且它是一个有效的React元素.
7pu*_*uns 75
试试这个
<div>{React.cloneElement(this.props.children, {...this.props})}</div>
Run Code Online (Sandbox Code Playgroud)
使用react-15.1对我有用.
Lyu*_*mir 61
查看所有其他答案
Context旨在共享可被视为React组件树的"全局"数据,例如当前经过身份验证的用户,主题或首选语言.1
免责声明:这是一个更新的答案,前一个使用旧的上下文API
它基于消费者/提供原则.首先,创建您的上下文
const { Provider, Consumer } = React.createContext(defaultValue);
Run Code Online (Sandbox Code Playgroud)
然后使用via
<Provider value={/* some value */}>
{children} /* potential consumers */
<Provider />
Run Code Online (Sandbox Code Playgroud)
和
<Consumer>
{value => /* render something based on the context value */}
</Consumer>
Run Code Online (Sandbox Code Playgroud)
作为提供者后代的所有消费者将在提供者的价值支柱发生变化时重新呈现.从Provider到其后代使用者的传播不受shouldComponentUpdate方法的约束,因此即使祖先组件退出更新,Consumer也会更新. 1
完整的例子,半伪代码.
import React from 'react';
const { Provider, Consumer } = React.createContext({ color: 'white' });
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: { color: 'black' },
};
}
render() {
return (
<Provider value={this.state.value}>
<Toolbar />
</Provider>
);
}
}
class Toolbar extends React.Component {
render() {
return (
<div>
<p> Consumer can be arbitrary levels deep </p>
<Consumer>
{value => <p> The toolbar will be in color {value.color} </p>}
</Consumer>
</div>
);
}
}
Run Code Online (Sandbox Code Playgroud)
1 https://facebook.github.io/react/docs/context.html
Ken*_*ong 43
通过对React 16.6的更新,您现在可以使用React.createContext和contextType.
import * as React from 'react';
// React.createContext accepts a defaultValue as the first param
const MyContext = React.createContext();
class Parent extends React.Component {
doSomething = (value) => {
// Do something here with value
};
render() {
return (
<MyContext.Provider value={{ doSomething: this.doSomething }}>
{this.props.children}
</MyContext.Provider>
);
}
}
class Child extends React.Component {
static contextType = MyContext;
onClick = () => {
this.context.doSomething(this.props.value);
};
render() {
return (
<div onClick={this.onClick}>{this.props.value}</div>
);
}
}
// Example of using Parent and Child
import * as React from 'react';
class SomeComponent extends React.Component {
render() {
return (
<Parent>
<Child value={1} />
<Child value={2} />
</Parent>
);
}
}
Run Code Online (Sandbox Code Playgroud)
React.createContext闪耀React.cloneElement案例无法处理嵌套组件的位置
class SomeComponent extends React.Component {
render() {
return (
<Parent>
<Child value={1} />
<SomeOtherComp><Child value={2} /></SomeOtherComp>
</Parent>
);
}
}
Run Code Online (Sandbox Code Playgroud)
Ali*_*eza 21
您可以使用React.cloneElement,在开始在应用程序中使用它之前,最好知道它是如何工作的.它已经介绍了React v0.13,请继续阅读以获取更多信息,以便为您完成这项工作:
<div>{React.cloneElement(this.props.children, {...this.props})}</div>
Run Code Online (Sandbox Code Playgroud)
因此,请参阅React文档中的内容,以了解它是如何工作的,以及如何使用它们:
在React v0.13 RC2中,我们将引入一个新的API,类似于React.addons.cloneWithProps,具有以下签名:
React.cloneElement(element, props, ...children);
Run Code Online (Sandbox Code Playgroud)
与cloneWithProps不同,这个新函数没有任何魔术内置行为来合并样式和className,原因与我们没有transferPropsTo的那个特性相同.没有人确定魔法事物的完整列表究竟是什么,这使得难以推断代码并且当样式具有不同的签名时难以重用(例如在即将推出的React Native中).
React.cloneElement几乎相当于:
<element.type {...element.props} {...props}>{children}</element.type>
Run Code Online (Sandbox Code Playgroud)
但是,与JSX和cloneWithProps不同,它还保留了refs.这意味着,如果你的孩子有一个参考,你不会意外地从你的祖先窃取它.您将获得与新元素相同的参考.
一种常见的模式是映射您的孩子并添加新的道具.报告了很多关于cloneWithProps丢失引用的问题,因此很难推断出你的代码.现在遵循与cloneElement相同的模式将按预期工作.例如:
var newChildren = React.Children.map(this.props.children, function(child) {
return React.cloneElement(child, { foo: true })
});
Run Code Online (Sandbox Code Playgroud)
注意:React.cloneElement(child,{ref:'newRef'})DOES会覆盖ref,因此除非使用callback-refs,否则两个父级仍然无法对同一个子进行引用.
这是进入React 0.13的关键特性,因为道具现在是不可变的.升级路径通常是克隆元素,但这样做可能会丢失引用.因此,我们需要一个更好的升级路径.当我们在Facebook上升级callites时,我们意识到我们需要这种方法.我们从社区获得了相同的反馈.因此,我们决定在最终版本发布之前制作另一个RC,以确保我们能够获得此版本.
我们计划最终弃用React.addons.cloneWithProps.我们还没有这样做,但这是一个开始考虑自己的用途并考虑使用React.cloneElement的好机会.在我们实际删除之前,我们一定会发布带有弃用通知的版本,因此不需要立即采取措施.
更在这里 ...
Ben*_*arp 14
const Parent = (props) => {
const attributeToAddOrReplace= "Some Value"
const childrenWithAdjustedProps = React.Children.map(props.children, child =>
React.cloneElement(child, { attributeToAddOrReplace})
);
return <div>{childrenWithAdjustedProps }</div>
}
Run Code Online (Sandbox Code Playgroud)
Context 允许您将 prop 传递给深层子组件,而无需通过中间的组件将其作为 prop 显式传递。
上下文有缺点:
使用可组合上下文
export const Context = createContext<any>(null);
export const ComposableContext = ({ children, ...otherProps }:{children:ReactNode, [x:string]:any}) => {
const context = useContext(Context)
return(
<Context.Provider {...context} value={{...context, ...otherProps}}>{children}</Context.Provider>
);
}
function App() {
return (
<Provider1>
<Provider2>
<Displayer />
</Provider2>
</Provider1>
);
}
const Provider1 =({children}:{children:ReactNode}) => (
<ComposableContext greeting="Hello">{children}</ComposableContext>
)
const Provider2 =({children}:{children:ReactNode}) => (
<ComposableContext name="world">{children}</ComposableContext>
)
const Displayer = () => {
const context = useContext(Context);
return <div>{context.greeting}, {context.name}</div>;
};
Run Code Online (Sandbox Code Playgroud)
小智 12
允许您进行财产转移的最佳方法children就像一个函数
例:
export const GrantParent = () => {
return (
<Parent>
{props => (
<ChildComponent {...props}>
Bla-bla-bla
</ChildComponent>
)}
</Parent>
)
}
export const Parent = ({ children }) => {
const somePropsHere = { //...any }
<>
{children(somePropsHere)}
</>
}
Run Code Online (Sandbox Code Playgroud)
我需要修复上面接受的答案,以使其使用该答案而不是此指针。在map函数范围内,此函数未定义doSomething函数。
var Parent = React.createClass({
doSomething: function() {
console.log('doSomething!');
},
render: function() {
var that = this;
var childrenWithProps = React.Children.map(this.props.children, function(child) {
return React.cloneElement(child, { doSomething: that.doSomething });
});
return <div>{childrenWithProps}</div>
}})
Run Code Online (Sandbox Code Playgroud)
更新:此修补程序适用于ECMAScript 5,在ES6中,不需要var that = this
你不再需要{this.props.children}.现在,您可以使用renderin 包装子组件Route并像往常一样传递道具:
<BrowserRouter>
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/posts">Posts</Link></li>
<li><Link to="/about">About</Link></li>
</ul>
<hr/>
<Route path="/" exact component={Home} />
<Route path="/posts" render={() => (
<Posts
value1={1}
value2={2}
data={this.state.data}
/>
)} />
<Route path="/about" component={About} />
</div>
</BrowserRouter>
Run Code Online (Sandbox Code Playgroud)
小智 6
考虑一个或多个孩子的更清洁方式
<div>
{ React.Children.map(this.props.children, child => React.cloneElement(child, {...this.props}))}
</div>
Run Code Online (Sandbox Code Playgroud)
如果你有多个想要传递 props 的孩子,你可以使用 React.Children.map 这样做:
render() {
let updatedChildren = React.Children.map(this.props.children,
(child) => {
return React.cloneElement(child, { newProp: newProp });
});
return (
<div>
{ updatedChildren }
</div>
);
}
Run Code Online (Sandbox Code Playgroud)
如果您的组件只有一个子组件,则无需映射,您可以直接使用 cloneElement:
render() {
return (
<div>
{
React.cloneElement(this.props.children, {
newProp: newProp
})
}
</div>
);
}
Run Code Online (Sandbox Code Playgroud)
这是我的版本,适用于单个、多个和无效的孩子。
const addPropsToChildren = (children, props) => {
const addPropsToChild = (child, props) => {
if (React.isValidElement(child)) {
return React.cloneElement(child, props);
} else {
console.log("Invalid element: ", child);
return child;
}
};
if (Array.isArray(children)) {
return children.map((child, ix) =>
addPropsToChild(child, { key: ix, ...props })
);
} else {
return addPropsToChild(children, props);
}
};
Run Code Online (Sandbox Code Playgroud)
使用示例:
https://codesandbox.io/s/loving-mcclintock-59emq?file=/src/ChildVsChildren.jsx:0-1069
父.jsx:
import React from 'react';
const doSomething = value => {};
const Parent = props => (
<div>
{
!props || !props.children
? <div>Loading... (required at least one child)</div>
: !props.children.length
? <props.children.type {...props.children.props} doSomething={doSomething} {...props}>{props.children}</props.children.type>
: props.children.map((child, key) =>
React.cloneElement(child, {...props, key, doSomething}))
}
</div>
);
Run Code Online (Sandbox Code Playgroud)
Child.jsx:
import React from 'react';
/* but better import doSomething right here,
or use some flux store (for example redux library) */
export default ({ doSomething, value }) => (
<div onClick={() => doSomething(value)}/>
);
Run Code Online (Sandbox Code Playgroud)
和 main.jsx:
import React from 'react';
import { render } from 'react-dom';
import Parent from './Parent';
import Child from './Child';
render(
<Parent>
<Child/>
<Child value='1'/>
<Child value='2'/>
</Parent>,
document.getElementById('...')
);
Run Code Online (Sandbox Code Playgroud)
请参阅此处的示例:https : //plnkr.co/edit/jJHQECrKRrtKlKYRpIWl?p=preview
除了@and_rest 答案之外,这就是我克隆子级并添加一个类的方法。
<div className="parent">
{React.Children.map(this.props.children, child => React.cloneElement(child, {className:'child'}))}
</div>
Run Code Online (Sandbox Code Playgroud)
小智 5
没有一个答案解决拥有非 React组件的子代的问题,例如文本字符串。解决方法可能是这样的:
// Render method of Parent component
render(){
let props = {
setAlert : () => {alert("It works")}
};
let childrenWithProps = React.Children.map( this.props.children, function(child) {
if (React.isValidElement(child)){
return React.cloneElement(child, props);
}
return child;
});
return <div>{childrenWithProps}</div>
}
Run Code Online (Sandbox Code Playgroud)
我认为渲染道具是处理这种情况的适当方法
您可以通过重构父级代码以使其看起来像这样,让父级提供子组件中使用的必要道具:
const Parent = ({children}) => {
const doSomething(value) => {}
return children({ doSomething })
}
Run Code Online (Sandbox Code Playgroud)
然后在子组件中您可以通过以下方式访问父组件提供的功能:
class Child extends React {
onClick() => { this.props.doSomething }
render() {
return (<div onClick={this.onClick}></div>);
}
}
Run Code Online (Sandbox Code Playgroud)
现在 fiianl 结构将如下所示:
<Parent>
{(doSomething) =>
(<Fragment>
<Child value="1" doSomething={doSomething}>
<Child value="2" doSomething={doSomething}>
<Fragment />
)}
</Parent>
Run Code Online (Sandbox Code Playgroud)
小智 5
受到上述所有答案的启发,这就是我所做的。我正在传递一些道具,如一些数据和一些组件。
import React from "react";
const Parent = ({ children }) => {
const { setCheckoutData } = actions.shop;
const { Input, FieldError } = libraries.theme.components.forms;
const onSubmit = (data) => {
setCheckoutData(data);
};
const childrenWithProps = React.Children.map(
children,
(child) =>
React.cloneElement(child, {
Input: Input,
FieldError: FieldError,
onSubmit: onSubmit,
})
);
return <>{childrenWithProps}</>;
};
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
367943 次 |
| 最近记录: |