1. 元素属性及props
1)组件可以接受任意 props,包括基本数据类型,React 元素以及函数。
2)组件接到父组件传来的props是只读的,绝不能够被修改的。这也叫做单向数据流。
3)组件中的标签元素与原生的dom元素不同,其会被转义为对象(虚拟dom)。组件元素的属性与事件与原生的也不尽相同。比如class
在 JSX 中被写作 className
,onclick
在 JSX 中被写作 onClick
、for
在 JSX 中被写作 htmlFor
。这些新构建的属性和事件一般都是用小驼峰的书写形式。
4)不要将 props “镜像”给 state,请考虑直接使用 props。否则可能会产生BUG(当一个派生 state 值被 setState
方法更新时,你的prop是否需要更新?)。
5)属性可使用展开表达式将所有props一次性赋值:
function App1() {
return <Greeting firstName="Ben" lastName="Hector" />;
}
// 等价于
function App2() {
const props = {firstName: 'Ben', lastName: 'Hector'};
return <Greeting {...props} />;
}
6)属性可以条件赋值
const Button = props => {
const { kind, ...other } = props;
const className = kind === "primary" ? "PrimaryButton" : "SecondaryButton";
return <button className={className} {...other} />;
};
const App = () => {
return (
<div>
<Button kind="primary" onClick={() => console.log("clicked!")}>
Hello World!
</Button>
</div>
);
};
2. state 和 setState
1)不要直接修改 State
,直接修改将不会重新渲染组件,而是应该使用 setState()
,类似于小程序中的setData()
。如this.state.comment = 'Hello';
是错误的。这里也能看出,React的数据是单向绑定且单向流动。
2)Class组件中,构造函数(constructor
)是唯一能够直接设置State
的地方。但现在一般都使用函数式组件及hook方法取代Class组件了。
3)Class组件中,setState
方法会浅合并对象(Object.assign
)。而在hook方法中,useState
导出的set方法不会自动合并更新对象。你可以用函数式的 setState 结合展开运算符来达到合并更新对象的效果。
const [state, setState] = useState({});
setState(prevState => {
// 也可以使用 Object.assign
return {...prevState, ...updatedValues};
});
3. Refs 和 Dom
1)Refs 创建
在React中也有refs,和Vue等其他框架一样,同样在标签中使用ref
属性。Refs 允许我们访问 DOM 节点或在 render 方法中创建的 React 元素。但非必要尽量避免能够使用props
解决的问题而使用refs
。
在Class组件中使用React.createRef()
来创建ref,或在函数式组件内部使用名为useRef()
的hook来创建ref。一般情况下可以使用ref的current
属性来访问该元素的dom节点或该自定义组件的实例。不可在函数组件父级中对函数组件使用ref,因为函数组件没有实例。
function MyFunctionComponent() {
// 这里必须声明 textInput,这样 ref 才可以引用它
const textInput = useRef(null);
function handleClick() {
textInput.current.focus();
}
// 这样做是正确的(在函数组件内部访问元素dom)
return (
<div>
<input
type="text"
ref={textInput} />
<input
type="button"
value="Focus the text input"
onClick={handleClick}
/>
</div>
);
}
class Parent extends React.Component {
constructor(props) {
super(props);
this.textInput = React.createRef();
}
render() {
// 这样做是错误的,无法访问的(在函数组件外部访问组件实例)
return (
<MyFunctionComponent ref={this.textInput} />
);
}
}
useRef()
比 ref
属性更有用。它可以很方便地保存任何可变值,其类似于在 class 中使用实例字段的方式。
这是因为它创建的是一个普通 Javascript 对象。而 useRef()
和自建一个 {current: ...}
对象的唯一区别是,useRef
会在每次渲染时返回同一个 ref 对象。
请记住,当 ref 对象内容发生变化时,useRef
并不会通知你。变更 .current
属性不会引发组件重新渲染。如果想要在 React 绑定或解绑 DOM 节点的 ref 时运行某些代码,则需要使用回调 ref 来实现。
2)回调 Refs
你可以在组件间传递回调形式的 Refs,也就是使用回调函数的形式创建Refs,这个函数中接受 React 组件实例或 HTML DOM 元素作为参数,以使它们能在其他地方被存储和访问。比如将回调函数通过props传递给子组件,这样父子组件都可以访问到子组件中的元素。
React 将在组件挂载时,会调用 ref
回调函数并传入 DOM 元素,当卸载时调用它并传入 null
。在 componentDidMount
或 componentDidUpdate
触发前,React 会保证 refs 一定是最新的。
回调Refs创建和使用
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = null;
this.setTextInputRef = element => {
this.textInput = element;
};
this.focusTextInput = () => {
// 使用原生 DOM API 使 text 输入框获得焦点
if (this.textInput) this.textInput.focus();
};
}
componentDidMount() {
// 组件挂载后,让文本框自动获得焦点
this.focusTextInput();
}
render() {
// 使用 `ref` 的回调函数将 text 输入框 DOM 节点的引用存储到 React
// 实例上(比如 this.textInput)
return (
<div>
<input
type="text"
ref={this.setTextInputRef}
/>
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}
父子组件共享Refs
function CustomTextInput(props) {
return (
<div>
<input ref={props.inputRef} />
</div>
);
}
class Parent extends React.Component {
render() {
return (
<CustomTextInput
inputRef={el => this.inputElement = el}
/>
);
}
}
在函数式组件中可以使用useCallback()
来创建具有变化监听的回调Ref
function MeasureExample() {
const [height, setHeight] = useState(0);
const measuredRef = useCallback(node => {
if (node !== null) {
setHeight(node.getBoundingClientRect().height);
}
}, []);
return (
<>
<h1 ref={measuredRef}>Hello, world</h1>
<h2>The above header is {Math.round(height)}px tall</h2>
</>
);
}
// 没有选择使用 useRef,因为当 ref 是一个对象时它并不会把当前 ref 的值的变化通知到我们。
// 注意到我们传递了 [] 作为 useCallback 的依赖列表。这确保了 ref callback 不会在再次渲染时改变,因此 React 不会在非必要的时候调用它。
// 在此示例中,当且仅当组件挂载和卸载时,callback ref 才会被调用,因为渲染的 <h1> 组件在整个重新渲染期间始终存在。
// 如果你希望在每次组件调整大小时都收到通知,则可能需要使用 ResizeObserver 或基于其构建的第三方 Hook。
4. 元素中表达式渲染的一些问题
false
, null
, undefined
, and true
是合法的子元素。但它们并不会被渲染。以下的 JSX 表达式渲染结果相同:
<div />
<div></div>
<div>{false}</div>
<div>{null}</div>
<div>{undefined}</div>
<div>{true}</div>
值得注意的是有一些 “falsy” 值(被类型转义),如数字 0
,仍然会被 React 渲染。这个特点和其他的一些框架会有些出入。
例如,以下代码并不会像你预期那样工作,因为当 props.messages
是空数组时,将会渲染为数字 0
:
// 如下会被渲染为数字0
<div>
{props.messages.length &&
<MessageList messages={props.messages} />
}
</div>
// 需要渲染右侧组件,&& 之前的表达式应总是布尔值:
<div>
{props.messages.length > 0 &&
<MessageList messages={props.messages} />
}
</div>
本文固定连接:https://code.zuifengyun.com/2022/08/2504.html,转载须征得作者授权。